← Laboratorio

Cómo evitar el spam en los formularios de PrestaShop (sin castigar a tus clientes)

Cómo frenar el spam de contacto y los registros falsos en PrestaShop: honeypot, control de tiempo, límite de intentos y captcha. Con el código de cada capa.

Publicado: 20/8/2026Revisado: 23/7/202615 min de lectura

Autor y revisor:

Ilustración editorial: Cómo evitar el spam en los formularios de PrestaShop (sin castigar a tus clientes)

El día que tu buzón dejó de servir

Hay un momento concreto en el que una tienda deja de leer su formulario de contacto. No es una decisión: simplemente, un día llegan cuarenta mensajes con enlaces a casinos, y al siguiente se abren por encima, y a la semana ya nadie los mira.

El problema no es el ruido. El problema es el mensaje número cuarenta y uno, el del cliente que preguntaba si el producto le sirve para su caso y que ya no va a comprar porque nadie le contestó.

Esta guía va de cerrar esa puerta sin cerrársela a los clientes. Está escrita para PrestaShop 8 y 9, pero casi todo aplica a cualquier tienda.

Por qué el reCAPTCHA gratuito no basta

Es el primer consejo que da todo el mundo, y no está mal. Está incompleto.

Los bots modernos no “resuelven” el captcha: lo esquivan. Un script que hace POST directo al endpoint de PrestaShop nunca ejecuta el JavaScript de Google, así que no genera token. Si tu validación solo comprueba el token cuando existe, el bot pasa por el hueco. Y si la haces obligatoria, cualquier cliente con JavaScript bloqueado o una extensión de privacidad se queda fuera sin entender por qué.

Hay un segundo problema, menos comentado y más serio: reCAPTCHA envía datos del visitante a Google antes de que haya consentido nada. La Agencia Española de Protección de Datos considera que los servicios de este tipo requieren base jurídica y transparencia, y varias autoridades europeas han sancionado su uso indiscriminado. Si lo cargas en todas las páginas por si acaso, tienes un problema de cookies además de uno de spam.

Y hay un tercero, puramente práctico: subir la exigencia castiga a quien menos lo merece. El usuario que más falla los captchas visuales es el que peor ve, el que va con prisa desde el móvil y el que tiene la conexión mala. Es decir, gente que compraría.

La conclusión no es “no uses reCAPTCHA”. Es que una sola capa no aguanta, y que la capa que molesta al cliente debería ser la última, no la primera.

Los cuatro sitios por donde entra

En PrestaShop el abuso no llega por un formulario, llega por cuatro, y cada uno recibe algo distinto:

Contacto. Spam publicitario clásico y campañas de enlaces. Es el más visible y el que primero se satura.

Registro de clientes. Cuentas falsas creadas en masa. Molesta menos de entrada, pero ensucia las estadísticas, infla la base de datos y, si tienes emails de bienvenida automáticos, puede quemarte la reputación de envío.

Newsletter. Suscripciones basura con direcciones inventadas. Cada rebote que acumulas empeora la entregabilidad del resto de tus correos, incluidos los de confirmación de pedido.

Recuperación de contraseña. El más ignorado y el más peligroso. Un script que pide recuperación mil veces contra direcciones reales convierte tu tienda en la herramienta de acoso de otro, y tu dominio en el que aparece en la bandeja de entrada de la víctima.

A esto se añaden, según la tienda, el formulario de direcciones y el de reseñas de producto.

Ordena por dónde te duele. Casi nadie necesita blindar los seis a la vez, y hacerlo de golpe es la forma más rápida de romper algo sin saber cuál.

La capa que casi nadie pone y filtra más: el señuelo

Un honeypot es un campo que existe en el HTML, está oculto para las personas y no debería llegar nunca relleno. Los bots que rellenan formularios de forma automática lo rellenan porque leen el DOM, no la pantalla.

En una plantilla de PrestaShop:

<div class="cd-hp" aria-hidden="true">
  <label for="cd_website">No rellenes este campo</label>
  <input type="text" id="cd_website" name="cd_website" tabindex="-1" autocomplete="off">
</div>

Y en el CSS, fuera de la vista sin usar display:none, que algunos bots detectan:

.cd-hp {
  position: absolute;
  left: -9999px;
  height: 0;
  overflow: hidden;
}

La validación en el controlador es una línea:

if (Tools::getValue('cd_website') !== '') {
    // Se descarta en silencio: responder "spam detectado" le dice al bot
    // exactamente qué cambiar en el siguiente intento.
    Tools::redirect('index.php?controller=contact&submitted=1');
}

Fíjate en el detalle del comentario, porque es lo que separa un honeypot que funciona de uno que dura dos semanas: si le dices al bot que lo has pillado, se adapta. Un envío bloqueado debe parecerse a un envío correcto.

Tres advertencias sobre el aria-hidden y el tabindex="-1": son obligatorios. Sin ellos, un lector de pantalla anuncia el campo y una persona ciega intentará rellenarlo, con lo que habrás construido un filtro que bloquea exactamente a quien no debe.

Control de tiempo: nadie escribe tan rápido

La segunda capa es todavía más barata. Al pintar el formulario, guarda cuándo se pintó. Al recibirlo, mira cuánto ha pasado.

// Al mostrar el formulario
$momento = time();
$firma = hash_hmac('sha256', (string) $momento, _COOKIE_KEY_);
// se envían como campos ocultos: cd_t y cd_s

// Al recibirlo
$momento = (int) Tools::getValue('cd_t');
$firma = (string) Tools::getValue('cd_s');
$esperada = hash_hmac('sha256', (string) $momento, _COOKIE_KEY_);

// La firma evita que el bot mande una marca de tiempo inventada.
if (!hash_equals($esperada, $firma) || (time() - $momento) < 3) {
    Tools::redirect('index.php?controller=contact&submitted=1');
}

La firma no es opcional. Sin ella, el campo de tiempo lo controla quien envía, y basta con mandar una marca de hace un minuto. Es el error típico de las implementaciones caseras: ponen el control temporal y lo dejan manipulable.

Tres segundos es conservador. Un formulario de contacto real rara vez se rellena en menos de quince, pero conviene ir holgado: alguien que pega un texto ya escrito y le da a enviar puede tardar cinco.

Limitar intentos, y por qué la sesión no vale

La tercera capa frena la fuerza bruta:

$ip = Tools::getRemoteAddr();
$desde = date('Y-m-d H:i:s', strtotime('-1 hour'));

$intentos = (int) Db::getInstance()->getValue(
    'SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'cd_envios`
      WHERE ip_hash = "' . pSQL(hash('sha256', $ip . _COOKIE_KEY_)) . '"
        AND creado_en > "' . pSQL($desde) . '"'
);

if ($intentos >= 5) {
    Tools::redirect('index.php?controller=contact&submitted=1');
}

Dos decisiones ahí que conviene entender:

El contador va en base de datos, no en sesión. Un bot no guarda cookies, así que cada envío le abre una sesión nueva y el contador nunca sube. Guardarlo en sesión es un límite que solo se aplica a los clientes legítimos, que es justo al revés de lo que quieres.

La IP se guarda como hash, no en claro. Una dirección IP es un dato personal, y guardarla en bruto para siempre en una tabla de spam es un tratamiento que tendrías que justificar. El hash con una sal —aquí, la clave de cookies de la tienda— te da lo que necesitas, que es contar, sin quedarte lo que no necesitas, que es saber quién.

Cuidado con las tiendas detrás de CDN o proxy: si no lees bien la cabecera del cliente real, todos tus visitantes comparten IP y el límite los bloquea a todos en cuanto pasen cinco. Comprueba qué devuelve Tools::getRemoteAddr() en tu instalación antes de fiarte.

Qué no funciona

Vale la pena decirlo, porque son consejos que circulan mucho:

Bloquear por país. El spam viene de servidores alquilados, y se alquilan en cualquier sitio. Bloqueando países pierdes clientes reales y no bloqueas casi nada.

Listas negras de dominios de correo. Se quedan viejas en semanas. Y el spammer que usa gmail.com no aparece en ninguna.

Preguntas tipo “¿cuánto es 2+2?”. Un script las resuelve mejor que una persona. Solo frenan a los bots de hace quince años.

Ocultar el formulario tras un clic. Frena a los bots que no ejecutan JavaScript, que son precisamente los que ya frenaba el honeypot, y estorba a los clientes que van desde el móvil.

Cambiar el nombre de los campos. Sirve una temporada contra scripts genéricos que buscan name="message". Contra cualquiera que mire tu HTML una vez, no.

Mide antes de apretar

Esta es la parte que casi nadie hace y la que más disgustos evita.

Cuando montes las capas, registra qué bloqueas y por qué motivo durante al menos dos semanas antes de subir la exigencia. Lo que buscas en ese registro no es cuánto spam paras —eso lo notas en el buzón— sino si estás parando algo que no deberías.

La señal de alarma es un pico de bloqueos por control temporal: suele significar que alguien pega texto preparado y envía rápido, y eso son clientes, no bots. La segunda señal es que los bloqueos por límite de intentos se concentren en pocas IPs con muchos envíos legítimos: ahí tienes el problema del proxy.

Guarda el motivo, la fecha y el hash de IP. Nada más. Y bórralo pasados noventa días: un registro de bloqueos que crece indefinidamente es una base de datos de comportamiento de tus visitantes que no te aporta nada y sí te obliga a explicarla.

Cuándo compensa un módulo

Todo lo anterior se puede montar a mano. La pregunta razonable es si merece la pena.

Hazlo a mano si tienes un solo formulario problemático, sabes tocar plantillas y controladores, y no te importa revisarlo cuando actualices PrestaShop. Son unas horas y aprendes cómo funciona tu tienda.

Un módulo compensa si te entra por varios formularios a la vez, necesitas el registro de bloqueos sin montártelo, o —el caso más común— no quieres que la próxima actualización de PrestaShop se lleve por delante tus cambios en las plantillas. Eso es lo que de verdad se paga: que siga funcionando dentro de un año.

Nosotros vendemos uno, Croqueta Shield, que hace exactamente esto por capas sobre los seis formularios, con reCAPTCHA v3 y reserva en v2, y con el visor de bloqueos en el panel. Lo digo con la misma honestidad con la que he escrito el resto: si tu problema es solo el formulario de contacto y te apañas con el código de arriba, no lo necesitas.

Metodología y fuentes

Lo descrito aquí procede del trabajo de mantenimiento sobre tiendas PrestaShop 8 y 9 y del código del propio módulo. Los fragmentos están simplificados para que se entiendan: en producción hay que añadir la gestión de errores y las comprobaciones de token de PrestaShop.

No aparece ni una cifra de “porcentaje de spam bloqueado” a propósito. Cualquier número de ese tipo depende del volumen y del tipo de abuso de cada tienda, y darlo como si fuera general sería inventárselo.

Referencias consultadas:

Revisado: 20 de agosto de 2026.

Ampliación editorial · revisión 2026

La decisión central de esta guía

¿Cómo frenar el spam de formularios en PrestaShop sin bloquear a clientes reales?

Con varias comprobaciones baratas que se suman: un campo señuelo, un control de tiempo firmado y un límite de intentos guardado en base de datos. El captcha se reserva para el final, porque es la única capa que el cliente nota. Una sola defensa no aguanta: los scripts que hacen POST directo nunca ejecutan el JavaScript del captcha.

Decisión

Empieza por el formulario que recibe el abuso, no por los seis. Sube la exigencia solo con el registro de bloqueos delante, y trata cualquier pico de bloqueos por tiempo como sospecha de falso positivo, no como éxito.

Evidencia

Mide bloqueos por motivo durante al menos dos semanas antes de endurecer nada. Lo que buscas no es cuánto spam paras —eso se nota en el buzón— sino si estás descartando mensajes legítimos. Guarda motivo, fecha y hash de IP, y bórralo a los noventa días.

Límite

No se dan porcentajes de spam bloqueado: dependen del volumen y del tipo de abuso de cada tienda. Bloquear por país o por listas de dominios no funciona y pierde clientes reales. El captcha envía datos a terceros y necesita consentimiento previo.

Carrito y panel visual que representan una operación ecommerce
Visual editorial relacionada con el sistema analizado en esta guía.

Marco técnico

Arquitectura de ecommerce orientada a compra y operación

Un ecommerce coordina descubrimiento, decisión, pago, inventario, entrega y postventa. La interfaz es solo la capa visible. La calidad depende de que producto, precio, stock, cliente y pedido tengan reglas claras y de que las excepciones puedan resolverse sin perder trazabilidad.

La plataforma se selecciona después de modelar catálogo, clientes, países, impuestos, pagos, transportistas e integraciones. PrestaShop, WooCommerce o Shopify pueden resolver bien escenarios diferentes. Comparar solo precio de licencia ignora módulos, hosting, soporte, comisiones, actualización y coste de salida.

La ficha de producto reduce incertidumbre. Nombre, imágenes, variantes, medidas, disponibilidad, entrega, devolución y soporte aparecen con una jerarquía coherente. Las descripciones duplicadas del fabricante no expresan experiencia ni diferencian. El contenido responde preguntas que llegan a ventas y utiliza datos estructurados solo cuando la información existe en pantalla.

Analítica para revisar compra, checkout y rentabilidad
La segunda imagen separa diagnóstico y aplicación para reducir fatiga de lectura.

El checkout elimina campos que no cambian la entrega o el pago, conserva datos ante error y comunica el coste antes del último paso. Compra como invitado, métodos rápidos y autocompletado pueden ayudar, pero se validan por dispositivo y mercado. Reducir pantallas no compensa una política confusa o un pago que falla.

Stock y pedido requieren reconciliación. La tienda no debe prometer disponibilidad que el sistema maestro no puede sostener. Cuando una integración falla, el equipo necesita alerta, contexto e instrucciones. Los reintentos no pueden duplicar pedidos y los estados visibles para el cliente deben corresponder con el trabajo real.

Lista de control antes de ejecutar

  1. 01
    Catálogo gobernado

    Taxonomía, atributos, variantes, imágenes y propietario están definidos.

  2. 02
    Compra móvil probada

    Búsqueda, ficha, carrito, teclado, pago y errores funcionan en pantalla pequeña.

  3. 03
    Coste transparente

    Impuestos, envío, plazos y devolución aparecen antes de confirmar.

  4. 04
    Stock reconciliable

    Existe un dato maestro y un procedimiento para resolver diferencias.

  5. 05
    SEO de ciclo completo

    Filtros, paginación, variantes, agotados y retirados tienen política explícita.

  6. 06
    Rentabilidad medida

    Compra se conecta con margen, comisión, logística, devolución y soporte.

Qué medir para saber si funciona

Observa búsqueda sin resultados, uso de filtros, selección de variante, añadir al carrito, inicio de checkout, error, pago y compra. Segmenta por dispositivo, método y origen. Un embudo agregado puede ocultar que una sola pasarela o navegador concentra el problema.

La operación añade exactitud de stock, pedidos retenidos, tiempo de preparación, entrega, devolución y contacto. La mejora de conversión solo es sostenible cuando el equipo puede servir el volumen y el margen resultante justifica adquisición y soporte.

Playbook propio de esta guía

Defensa por capas en los formularios de PrestaShop

Esta ampliación desarrolla el problema específico de cómo evitar el spam en los formularios de prestashop (sin castigar a tus clientes) y no se reutiliza en otras entradas del Laboratorio.

Localizar la puerta antes de cerrarla

El abuso entra por cuatro sitios distintos y cada uno recibe algo diferente: contacto recibe publicidad y enlaces, registro recibe cuentas falsas que ensucian la base de datos, newsletter recibe direcciones inventadas que degradan la entregabilidad del resto de tus correos, y recuperación de contraseña se usa para acosar a terceros desde tu dominio. Antes de instalar nada, mira de dónde vienen los mensajes.

Ordena por dónde duele. Casi ninguna tienda necesita blindar los seis formularios a la vez, y hacerlo de golpe es la forma más rápida de romper algo sin saber cuál de los cambios ha sido. Empieza por el que satura el buzón y deja los demás para cuando el primero esté medido.

Capas que no molestan al cliente

El campo señuelo es un input oculto por posición, nunca con display none, con aria-hidden y tabindex negativo para que un lector de pantalla no lo anuncie. Si llega relleno, se descarta el envío en silencio: responder que se ha detectado spam le dice al bot exactamente qué cambiar en el siguiente intento.

El control de tiempo guarda cuándo se pintó el formulario y rechaza lo que llega en menos de tres segundos. La marca debe ir firmada con HMAC; sin firma la controla quien envía y basta con mandar una hora inventada. Es el error habitual de las implementaciones caseras: ponen el control y lo dejan manipulable.

Límite de intentos y captcha, en ese orden

El contador de intentos va en base de datos y no en sesión: un bot no guarda cookies, así que cada envío le abre una sesión nueva y el contador nunca sube. Guardarlo en sesión produce un límite que solo se aplica a los clientes legítimos. La IP se almacena como hash con sal, porque para contar no hace falta saber quién.

El captcha va al final porque es la capa que el cliente nota. reCAPTCHA v3 puntúa sin fricción y solo pide comprobación explícita cuando la puntuación baja. Ten en cuenta que envía datos a Google antes de que el visitante consienta nada, así que entra en la política de cookies.

Cuaderno de campo: el filtro que no se come los mensajes buenos

El coste del spam de formularios no es el ruido, es el mensaje que se pierde entre el ruido. Una tienda deja de leer su buzón de contacto de forma gradual: primero se abren los mensajes por encima, luego se revisan una vez al día, y al final nadie los mira. El daño no aparece en ninguna métrica, porque el cliente que preguntó si el producto le servía y no recibió respuesta no deja rastro: simplemente compra en otro sitio. Por eso el objetivo de un filtro no es bloquear el máximo posible, sino recuperar la confianza en el buzón, que es una cosa distinta y se mide distinto.

La defensa que funciona es por capas y en orden de fricción creciente. Primero lo que el cliente no nota: el campo señuelo y el control de tiempo firmado, que juntos filtran el grueso de los envíos automatizados y no añaden ni un clic. Después el límite de intentos, que tampoco se nota salvo que alguien envíe cinco formularios en una hora. Y solo al final el captcha, que es la única capa visible y la que castiga a quien peor ve, va con prisa desde el móvil o tiene mala conexión. Invertir ese orden —empezar por el captcha porque es lo que todo el mundo recomienda— es lo que produce tiendas con captcha y con spam a la vez, porque un script que hace POST directo al endpoint nunca ejecuta el JavaScript de Google.

La parte que casi nadie hace es medir antes de apretar. Durante las dos primeras semanas conviene registrar cada bloqueo con su motivo y no tocar la configuración. Lo que se busca en ese registro no es cuánto spam se para, sino si se está parando algo que no se debería. Un pico de bloqueos por control temporal suele significar que alguien pega un texto ya escrito y envía rápido, y eso son clientes. Bloqueos por límite de intentos concentrados en pocas direcciones con envíos legítimos suele significar que la tienda está detrás de un proxy y todos los visitantes comparten IP. Ninguna de las dos cosas se ve desde el buzón: solo desde el registro.

Sobre la decisión de resolverlo a mano o con un módulo, el criterio honesto no es técnico sino de mantenimiento. El código de las capas es corto y se entiende en una tarde. Lo que se paga al comprar un módulo no es el algoritmo: es que la próxima actualización de PrestaShop no se lleve por delante los cambios hechos en las plantillas, y que el registro de bloqueos exista sin tener que montarlo. Si el problema es un solo formulario y hay quien mantenga el código, hacerlo a mano es perfectamente razonable y además se aprende cómo funciona la tienda por dentro.

Comprobaciones antes de cerrar

  1. 01
    El bloqueo se parece a un envío correcto

    Ni mensaje de error distinto, ni código de estado distinto, ni tiempo de respuesta distinto. Cualquier señal es información para afinar el siguiente intento.

  2. 02
    La marca de tiempo va firmada

    Sin HMAC, el control temporal lo decide quien envía el formulario.

  3. 03
    El contador vive en base de datos

    En sesión solo limita a quien acepta cookies, es decir, a tus clientes.

  4. 04
    La IP se guarda como hash

    Es un dato personal. Para contar intentos basta con el hash y una sal.

  5. 05
    El registro se borra a los noventa días

    Un histórico de bloqueos que crece sin fin es un registro de comportamiento que no aporta y sí obliga a justificarlo.

  6. 06
    El señuelo es accesible

    Sin aria-hidden y tabindex negativo, el filtro bloquea justamente a quien usa lector de pantalla.

Revisa este playbook cuando cambie el tipo de abuso o después de actualizar PrestaShop, porque las actualizaciones pueden llevarse por delante los cambios hechos a mano en las plantillas. Si empiezan a aparecer bloqueos por control temporal en horario comercial, baja la exigencia antes de seguir subiéndola: probablemente estés descartando clientes.

Pasos del procedimiento

  1. 1. Identifica qué formulario recibe el abuso

    Antes de instalar nada, mira de dónde vienen los mensajes: contacto, registro de clientes, newsletter o recuperación de contraseña. Cada uno recibe un tipo de abuso distinto y se defiende de forma distinta.

  2. 2. Añade un campo señuelo (honeypot)

    Un campo oculto por CSS que las personas no ven y muchos bots rellenan. Si viene relleno, se descarta el envío sin avisar. Cuesta diez líneas y filtra el grueso del spam automatizado.

  3. 3. Controla el tiempo entre carga y envío

    Guarda una marca de tiempo firmada al pintar el formulario y rechaza los envíos que llegan en menos de tres segundos. Ninguna persona rellena un formulario de contacto en dos segundos.

  4. 4. Limita los intentos por IP y por sesión

    Cinco envíos por hora y dirección es holgado para un cliente real y letal para un script. Guarda el contador en base de datos, no en sesión, porque los bots no mantienen cookies.

  5. 5. Añade reCAPTCHA v3 solo donde haga falta

    Puntúa sin molestar al usuario y solo pide comprobación explícita cuando la puntuación es baja. Recuerda que envía datos a Google y necesita consentimiento previo en la política de cookies.

  6. 6. Mide qué se bloquea antes de subir la exigencia

    Registra los bloqueos con su motivo durante dos semanas. Si el filtro está descartando mensajes legítimos lo verás ahí, y no cuando un cliente se queje de que no le contestas.

Control editorial

Metodología y fuentes

Las capas descritas proceden de mantenimiento sobre tiendas PrestaShop 8 y 9. No se dan porcentajes de spam bloqueado: dependen del volumen y del tipo de abuso de cada tienda, y darlos como cifra general seria inventarlos.