Guías / Hosting estático
Cómo frenar los registros falsos en los formularios de tu sitio web
Para frenar los registros falsos en el formulario de un sitio web, usa dos capas. Primero, activa el honeypot y reCAPTCHA en los ajustes de formularios de Static.app, que frenan la mayoría de los bots. Después, comprueba cada dirección de correo con un verificador de email como el de Unspam, que detecta los buzones desechables, los dominios mal escritos y los buzones que no existen. En Static.app la comprobación se hace en una pequeña función que recibe cada entrada por webhook, así que tu token de API nunca aparece en la página.
Por qué se cuelan los registros falsos
Los registros falsos llegan por tres vías, y cada una necesita una solución distinta:
- Los bots rellenan todos los formularios que encuentran, a menudo cientos de veces al día. Un honeypot o un CAPTCHA los frena.
- Las direcciones desechables las usan personas reales que quieren una descarga o una prueba sin dar su correo de verdad. Un CAPTCHA no las frena, porque quien escribe es una persona.
- Las erratas y los buzones muertos, como
[email protected], vienen de personas con buena intención. Cada correo que les envías rebota, y demasiados rebotes dañan tu reputación como remitente.
Los ajustes del formulario se encargan del primer grupo. Un verificador de email se encarga de los otros dos.
Paso 1: Frena los bots en los ajustes del formulario
Cada sitio web en Static.app tiene dos filtros contra bots para los formularios creados con el atributo static-form. Abre tu sitio web en el panel y entra en sus ajustes de formularios.
- Honeypot: en Nombre del campo honeypot (opcional), escribe un nombre como
_honeyy guarda. El script del formulario añade un campo oculto con ese nombre. Las personas no lo ven, así que lo que lo rellena es un bot, y su entrada se descarta sin avisar. - reCAPTCHA v3: marca Activar reCAPTCHA v3, crea un par de claves v3 para tu dominio en la consola de administración de Google reCAPTCHA y pega la clave del sitio y la clave secreta. Los visitantes no ven ningún desafío. Las entradas que Google puntúa como probables bots se rechazan.
Para un formulario de contacto sencillo, esto suele bastar. Sigue leyendo si tu formulario recoge registros para una newsletter, una lista de espera o una prueba, donde cada dirección mala te cuesta.
Paso 2: Comprueba cada dirección con el verificador de email de Unspam
El verificador de email de Unspam comprueba la sintaxis y el dominio de una dirección, el servidor de correo y si el buzón acepta mensajes. También marca las direcciones desechables, genéricas y de dominios catch-all. Cada respuesta incluye un status, un reason y una acción sugerida en action, así que tu código no tiene que interpretar los detalles.
Por qué la comprobación no puede hacerse en la página
La API de Unspam necesita un token secreto en cada petición. Todo lo que hay en el JavaScript de tu página lo pueden leer los visitantes, así que un token puesto ahí se podría copiar y usar para gastar tus créditos. Además, la API no acepta peticiones desde el navegador. Como Static.app no ejecuta código en el servidor, la comprobación se hace en una pequeña función en un servicio como Cloudflare Workers. Static.app le envía cada entrada nueva por webhook.
Qué necesitas
- Una cuenta de Unspam con acceso a la API, que viene con el plan Custom. Consulta los precios de Unspam para ver las condiciones actuales. Crea un token de API en los ajustes de tu cuenta de Unspam.
- Una cuenta de Cloudflare. Una función tan pequeña cabe en el plan gratuito de Workers para la mayoría de los sitios web.
- Un destino para los registros válidos, como la URL de webhook de tu herramienta de email marketing, Zapier o Make.
Crea la función
En Cloudflare, crea un Worker nuevo, sustituye su código por el de abajo y despliégalo. Después añade cuatro variables en los ajustes del Worker, y marca las dos primeras como secretas:
UNSPAM_TOKEN: tu token de API de UnspamWEBHOOK_SECRET: una cadena aleatoria larga. Pegarás la misma cadena en Static.app.FORWARD_URL: adónde deben ir los registros válidosEMAIL_FIELD: elnamedel campo de correo de tu formulario, normalmenteemail
const CHECKS = 'https://api.unspam.email/ext/v2/email-verification/instant-checks';
export default {
async fetch(request, env, ctx) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
const body = await request.text();
const signature = request.headers.get('X-Webhook-Signature');
if (!(await validSignature(body, signature, env.WEBHOOK_SECRET))) {
return new Response('Bad signature', { status: 401 });
}
// Responde a Static.app enseguida y verifica en segundo plano,
// para que una comprobación lenta no agote el tiempo del webhook.
ctx.waitUntil(handleEntry(JSON.parse(body), env));
return new Response('ok');
},
};
async function handleEntry(entry, env) {
const email = String(entry.fields?.[env.EMAIL_FIELD || 'email'] || '').trim();
if (!email) return;
const result = await verify(email, env);
if (shouldBlock(result)) return;
await fetch(env.FORWARD_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...entry, verification: result }),
});
}
async function verify(email, env) {
const headers = {
Authorization: `Bearer ${env.UNSPAM_TOKEN}`,
'Content-Type': 'application/json',
};
try {
let res = await fetch(CHECKS, { method: 'POST', headers, body: JSON.stringify({ email }) });
if (!res.ok) return null;
let check = await res.json();
for (let i = 0; i < 10 && check.status === 'pending'; i++) {
await new Promise((resolve) => setTimeout(resolve, 2000));
res = await fetch(`${CHECKS}/${check.id}`, { headers });
if (!res.ok) return null;
check = await res.json();
}
return check.status === 'completed' ? check.result : null;
} catch {
return null;
}
}
function shouldBlock(result) {
if (!result) return false; // sin respuesta: deja pasar el registro
if (result.action === 'remove') return true;
const flags = result.flags || [];
return flags.includes('disposable') || flags.includes('throwaway');
}
async function validSignature(body, header, secret) {
if (!secret || !header || !/^sha256=[0-9a-f]{64}$/i.test(header)) return false;
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']
);
const bytes = new Uint8Array(header.slice(7).match(/../g).map((h) => parseInt(h, 16)));
return crypto.subtle.verify('HMAC', key, bytes, encoder.encode(body));
}
La función comprueba primero la firma, así que solo Static.app puede enviarle entradas. Responde en el acto y hace la comprobación en segundo plano, porque Static.app espera solo 10 segundos una respuesta y, si no la recibe, vuelve a enviar la entrada. Cada reenvío costaría otra comprobación. El verificador responde al cabo de unos segundos, así que la función vuelve a preguntar cada 2 segundos, durante un máximo de 20.
Conecta el webhook en Static.app
- Abre tu sitio web en el panel, entra en las apps de sus formularios, haz clic en Añadir app y elige Webhooks.
- Ponle un nombre al webhook y pega la dirección de tu Worker como URL.
- Elige el formulario de registro en Formulario.
- En Secreto (opcional), pega la misma cadena que usaste en
WEBHOOK_SECRET. Así Static.app firma cada petición, y la función rechaza todo lo que no vaya firmado. - Comprueba que el webhook está Activo y guarda.
Envía el formulario una vez con tu propia dirección y otra con una desechable. Solo la primera debería llegar a tu lista. Si no llega nada, abre los registros del Worker en Cloudflare.
Decide qué cuenta como falso
La función de arriba usa la acción que sugiere Unspam en action. Esto es lo que significa cada valor y lo que hace el código con él:
keep: el buzón existe y acepta correo. El registro pasa.review: la dirección podría funcionar, por ejemplo en un dominio catch-all que acepta cualquier nombre. El registro pasa, porque muchos dominios de empresa funcionan así.remove: la dirección está mal, el dominio no existe o el buzón la rechazó. El registro se descarta.retry, o sin respuesta: el servidor de correo no contestó a tiempo. El registro pasa, porque perder a un cliente real cuesta más que dejar entrar una dirección falsa.
Las direcciones marcadas como disposable o throwaway se descartan sea cual sea su acción, porque suelen dejar de funcionar en pocas horas. Si también quieres rechazar buzones compartidos como info@, añade la marca role a shouldBlock. Cuando Unspam detecta una probable errata, el resultado incluye did_you_mean con la dirección corregida, que puedes guardar junto a la entrada.
Lo que esta configuración no cambia
El webhook se ejecuta después de que Static.app haya guardado la entrada, así que las entradas falsas siguen apareciendo en las entradas de formulario de tu sitio web, y el aviso por correo de cada una se sigue enviando. La función las mantiene fuera de las herramientas que vienen después: tu lista de correo, tu CRM, tus correos de bienvenida.
Si solo quieres enterarte de los registros reales, desmarca Activar notificaciones por correo en los ajustes del formulario y haz que la herramienta de FORWARD_URL te avise. Para ordenar el panel, selecciona las entradas que no quieras y usa Marcar como spam.
Limpia los registros que ya tienes
No necesitas código para comprobar los registros que recogiste antes de montar esto:
- En los formularios de tu sitio web, abre el menú del formulario y elige Guardar como CSV.
- Sube el archivo a la verificación de listas de Unspam. Encuentra sola la columna de direcciones, y no cobra los duplicados ni las filas mal formadas.
- Quita de tu lista de correo las direcciones marcadas como remove, y marca sus entradas como spam en Static.app.
Preguntas frecuentes
Las respuestas siguientes tratan las claves de API en la página, la velocidad del formulario, hasta dónde llega la comprobación y cuánto cuesta.
¿Puedo llamar al verificador de email desde el JavaScript de mi formulario?
No. Todos los visitantes podrían ver el token de API, y la API de Unspam no acepta peticiones desde el navegador. Además, los bots envían las entradas directamente al punto de recepción del formulario sin ejecutar tu JavaScript, así que una comprobación en la página tampoco los frenaría. Una función que recibe el webhook es el lugar donde la comprobación no se puede saltar.
¿Esto hará más lento mi formulario?
No. Los visitantes ven el mensaje de éxito de siempre en cuanto Static.app guarda la entrada. La comprobación ocurre después, en la función.
¿Un verificador de email detecta todas las direcciones falsas?
No. Los dominios catch-all aceptan cualquier dirección, así que no se pueden comprobar buzón por buzón. Yahoo y AOL tampoco confirman si un buzón existe. Y quien se registra con una dirección que funciona pero que nunca lee también pasará. Junto con el honeypot y reCAPTCHA, la comprobación elimina la mayor parte de la basura, y el doble opt-in de tu herramienta de email detecta lo que quede.
¿Lo necesito para un formulario de contacto?
Normalmente no. Los mensajes de contacto no van a una lista de correo, así que una dirección mala te cuesta poco. El honeypot y reCAPTCHA bastan. La comprobación de direcciones compensa en formularios de registro, de lista de espera y de descarga.
¿Cuánto cuesta?
Los formularios, el honeypot, reCAPTCHA y los webhooks forman parte de tu sitio web en Static.app. En Unspam, las primeras comprobaciones de cada día son gratis. Después, cada respuesta definitiva usa una unidad de la cuota de tu plan o de tus créditos, y las respuestas desconocidas nunca se cobran. Su API viene con el plan Custom. El plan gratuito de Workers de Cloudflare basta para la mayoría de los volúmenes de registro. Para poner un formulario en tu sitio web, consulta Static Forms.
¿Quieres probarlo?
Añade un formulario a tu sitio web