Tu formulario de contacto funciona. Ese es justo el problema. A la semana de publicarlo empieza a acumular propuestas de SEO, sorteos de criptomonedas y mensajes que no son más que tres enlaces y la palabra “hola”.
El reflejo es añadirle un CAPTCHA. Antes de hacerlo, conviene saber lo que eso te cuesta de verdad, y que las técnicas que atrapan más spam son invisibles para tus visitantes.
Lo que cuesta de verdad un CAPTCHA
Un CAPTCHA no sale gratis. Lo pagas con envíos que nunca llegan.
Quienes más probablemente abandonan un formulario al toparse con un acertijo son justo los que menos te interesa perder: alguien con mala conexión desde el móvil, alguien que usa un lector de pantalla, alguien cuya configuración de privacidad hace que el desafío se repita sin fin. Esa gente no te escribe para quejarse. Simplemente se va, y tú nunca te enteras de que existió ese mensaje.
Mientras tanto, el spam que de verdad hace daño (el dirigido, escrito por una persona a la que pagan por rellenar formularios) atraviesa el CAPTCHA sin despeinarse. Los servicios que los resuelven cuestan una fracción de céntimo por acertijo. Has añadido fricción a tus visitantes reales y un coste irrisorio a tus peores remitentes.
Así que el objetivo no es “bloquear todos los bots”. Es atrapar en silencio el ruido automatizado de gran volumen y ser prudente con todo lo demás.
Técnica 1: El honeypot
Un honeypot es un campo de formulario que ninguna persona puede ver. Los bots analizan tu HTML y rellenan todo lo que parezca rellenable. Los visitantes reales nunca lo tocan porque no se muestra en ningún sitio al que puedan llegar.
<!-- Los bots rellenan esto; las personas nunca lo ven -->
<input
type="text"
name="botcheck"
style="position:absolute; left:-9999px;"
tabindex="-1"
autocomplete="off"
aria-hidden="true"
/>
Cuatro detalles en ese marcado, y cada uno importa:
- Colocado fuera de la pantalla, no con
display:none. Algunos bots se saltan a propósito los inputs ocultos. Sacarlo del viewport resulta menos evidente. tabindex="-1"evita que quien navega con teclado acabe tabulando a un campo que no puede ver.aria-hidden="true"evita que los lectores de pantalla lo anuncien. Sin esto, tu honeypot es un fallo de accesibilidad: una persona ciega oye un campo misterioso, lo rellena y queda filtrada en silencio. Es el peor resultado posible.autocomplete="off"impide que el navegador lo rellene amablemente para una persona real.
La trampa que nadie menciona: el nombre del campo tiene que coincidir con el que comprueba tu backend. Si te inventas tu propio nombre, el servidor no sabe que es un honeypot y lo guarda como un campo más. El formulario se ve bien y no filtra nada. Formpaste busca botcheck; uses lo que uses, consulta la documentación de tu proveedor en lugar de adivinar.
Técnica 2: La comprobación de tiempo
Las personas tardan en rellenar un formulario. Los bots envían al instante.
Registra cuándo se cargó la página, mándalo junto al envío y compáralo al llegar:
<input type="hidden" name="_ts" id="form-ts" />
<script>
document.getElementById("form-ts").value = Date.now();
</script>
El umbral debe ser generoso. Formpaste considera sospechoso cualquier envío por debajo de tres segundos. Es deliberadamente bajo. Quien pega un mensaje ya preparado en un formulario de un solo campo va rápido, y no quieres penalizarlo. Tres segundos atrapan los envíos automatizados sin molestar a una persona con prisa.
Fíjate en qué ocurre cuando falta la marca de tiempo: suma cero puntos en lugar de contar en contra del remitente. Quien tiene JavaScript desactivado no debería ser tratado como un bot.
Técnica 3: Densidad de enlaces
La mayor parte del spam existe para colocar enlaces. Contarlos es barato y sorprendentemente eficaz:
const URL_RE = /https?:\/\/[^\s]+/gi;
const links = (text.match(URL_RE) ?? []).length;
Formpaste lo puntúa como una curva y no como un acantilado: dos enlaces suman la mitad, tres o más suman el total. Un cliente real manda de vez en cuando un enlace, a veces dos. Un mensaje con cuatro rara vez es alguien preguntando por tus precios.
Técnica 4: Palabras clave y patrones estructurales
La coincidencia por palabras clave es la técnica más obvia y la más sobrevalorada, así que necesita los límites más estrictos.
Las palabras sueltas se comparan por límites de palabra, no como subcadena. Este detalle no te lo puedes saltar. Un text.includes("cialis") ingenuo no da problemas, pero que includes("spec") coincida dentro de “especialista” es justo la clase de fallo que se come correo legítimo sin hacer ruido. Las frases de varias palabras como "buy backlinks" sí se comparan como subcadena, porque la frase en sí es la señal.
La ofuscación se trata aparte, porque los spammers escriben v1agra y c4sino:
{ canonical: "viagra", re: /(?<![a-z])v[i1!|]agr[a4@](?![a-z])/i }
Los patrones estructurales detectan la forma en lugar del vocabulario, con umbrales deliberadamente conservadores:
// gritar: 4+ palabras seguidas en MAYÚSCULAS, de 3+ letras cada una
{ name: "shouting", re: /\b[A-Z]{3,}\b(?:\s+\b[A-Z]{3,}\b){3,}/ }
// puntuación excesiva: 4+ veces el mismo !, ? o $ seguido
{ name: "excessive_punctuation", re: /([!?$])\1{3,}/ }
Ambos están ajustados para que un mensaje normal pase. Una frase escrita con enfado en mayúsculas no dispara la regla de gritar, y “ASAP” o “URL” tampoco, porque las siglas suelen ir sueltas y no en una cadena de cuatro. Quien escribe “¡ayuda!!” no llama la atención; cuatro signos de exclamación seguidos son otra cosa.
La regla que importa más que cualquier comprobación
Aquí viene la parte que casi todos los consejos sobre filtrado de spam se saltan, y es lo que separa un filtro que ayuda de uno que te hace perder negocio en silencio.
Ninguna señal débil aislada debería rechazar por sí sola un mensaje.
Todas las comprobaciones anteriores producen falsos positivos. Hay gente real que manda enlaces. Hay gente real que usa la palabra “casino” porque regenta un casino. Hay gente real que rellena formularios deprisa. Si cualquiera de esas cosas provoca un rechazo, perderás mensajes auténticos y nunca te enterarás, porque quien los envía da por hecho que te llegaron.
La solución es puntuar en vez de juzgar. Cada señal suma puntos; solo decide el total. Estos son los pesos que usa Formpaste:
| Señal | Puntos |
|---|---|
| Honeypot relleno | 100 |
| Enviado en menos de 3 segundos | 40 |
| Duplicado de un envío reciente | 40 |
| Densidad de enlaces | 30 |
| Correo desechable | 30 |
| Palabras clave de spam | 25 |
| Patrones estructurales | 25 |
El umbral de cuarentena es 100.
Al leer esos números se entiende el diseño. Las palabras clave por sí solas suman 25, muy lejos de 100. Un mensaje puede mencionar “investment opportunity” y aun así llegar a tu bandeja, porque una señal débil aislada no es una prueba. Pero palabras clave más tres enlaces más una dirección desechable suman 80, y añadir un envío rápido lo lleva a 120. Ahí ya es un patrón, no una casualidad.
El honeypot es la excepción deliberada con 100 puntos: rellenar un campo invisible no es algo que una persona haga por accidente, así que es concluyente por sí mismo. Aun así, pasa por la misma ruta aditiva y no por una rama especial del código. Un solo camino, sin sorpresas.
Cuarentena, no borrado
Incluso una puntuación bien ajustada se equivoca a veces. Así que la pregunta es: ¿qué haces con un mensaje del que estás un 80 % seguro de que es spam?
La respuesta equivocada es borrarlo. Descartarlo en silencio significa que un cliente real te escribió, vio un mensaje de éxito y espera una respuesta que nunca llegará. No puedes detectar ese fallo. No puedes medirlo. Simplemente pierdes el negocio y no te enteras.
La mejor respuesta es una carpeta de cuarentena. Los mensajes sospechosos se retienen y siguen visibles en lugar de destruirse, así que un falso positivo cuesta un clic de revisión en vez de un cliente. En Formpaste, la revisión de la cuarentena está disponible en el plan gratuito (solo lectura), y liberar un mensaje de vuelta a tu bandeja, lo que además añade a ese remitente a la lista de confianza, es una función de Pro.
Sea cual sea tu proveedor, la pregunta que hay que hacer es: ¿qué le pasa a un mensaje que parece sospechoso pero no lo es? Si la respuesta es “se borra”, estás cambiando un problema invisible por uno visible.
Lo que no puedes hacer sin servidor
Dos de estas cosas necesitan un backend, y conviene decirlo claro.
El límite de frecuencia necesita estado compartido entre peticiones. Formpaste aplica varias capas: 5 envíos por cada 10 segundos y clave de formulario, 10 por minuto y IP, más un límite sostenido por clave que detecta un formulario machacado de forma constante en vez de a ráfagas. Nada de esto es posible desde una página estática.
La detección de duplicados tiene que recordar lo que se envió antes, lo que de nuevo implica almacenamiento. Vale 40 puntos porque el mismo mensaje enviado repetidamente es una de las señales más fuertes que existen.
El marcado del honeypot y el campo de tiempo puedes añadirlos hoy mismo a cualquier página HTML estática. La puntuación, el límite de frecuencia y la cuarentena necesitan algo en el servidor, que es exactamente para lo que existe un backend de formularios.
Juntándolo todo
Si te quedas con una sola idea: puntúa, no juzgues. Reúne varias señales débiles, súmalas y actúa solo según el total. Y guarda los casos dudosos en algún sitio donde puedas verlos en lugar de borrarlos.
Ese enfoque atrapa la inmensa mayoría del spam automatizado, no le cuesta nada a tus visitantes reales y falla de forma segura cuando se equivoca. Sin acertijos.
Si estás añadiendo un formulario a un sitio estático y quieres el montaje completo, la guía paso a paso para un formulario de contacto sin backend cubre el marcado, y el filtrado de spam viene activado de serie.