Síntoma y contexto
El formulario de contacto es la pieza que convierte una visita en un mensaje útil. Cuando deja de funcionar, el síntoma puede ser muy distinto según dónde se rompa la cadena: el visitante pulsa Enviar y la página no reacciona; aparece un mensaje genérico de error; la petición parece completarse pero el correo nunca llega; o el envío funciona en tu ordenador pero no en el móvil de un cliente.
Este fallo es frecuente tras un rediseño, un cambio de hosting, la migración de un servicio de formularios (por ejemplo, de un endpoint propio a Formspree o Netlify Forms) o la activación de un proxy como Cloudflare sin revisar las reglas. También aparece cuando alguien añade validación JavaScript sin probar el flujo completo, o cuando el dominio del remitente del correo automático no tiene SPF y DKIM bien configurados y el mensaje cae en spam sin que nadie lo note.
La buena noticia es que la mayoría de casos se acotan en minutos si miras la petición HTTP real en lugar de adivinar. Un formulario estático con action y method="post" bien puestos suele ser el camino más fiable. Cuando entra JavaScript, reCAPTCHA, envío por fetch o integraciones con CRM, los puntos de fallo se multiplican pero siguen siendo localizables con la checklist de esta guía.
Antes de tocar producción, identifica qué esperabas que ocurriera: ¿redirección a una página de gracias?, ¿mensaje en la misma página?, ¿correo al buzón de soporte?, ¿entrada en una hoja de cálculo? Sin esa expectativa clara es fácil dar por arreglado un envío que llega al panel del proveedor pero no al correo que revisas cada mañana.
Causas probables
Action o endpoint incorrecto
El atributo action del elemento <form> debe apuntar exactamente a la URL que acepta POST del servicio elegido. Un ID de Formspree mal copiado, una barra final de más o menos, o un endpoint de staging en lugar de producción hacen que el navegador envíe los datos al vacío o a un formulario desactivado. Si migraste de proveedor, revisa que no quede el action antiguo en una plantilla duplicada.
JavaScript que bloquea el envío
Scripts que escuchan el evento submit y llaman a preventDefault() sin completar después el envío (por fetch, por crear un formulario oculto o por delegar al action nativo) dejan el botón inerte. Lo mismo ocurre con validadores personalizados que marcan el formulario como inválido sin mostrar mensaje claro al usuario. Abre la consola del navegador: errores de JavaScript no capturados pueden interrumpir todo el flujo antes de la petición de red.
Validación HTML5 demasiado estricta
Campos required, type="email", pattern o minlength impiden el envío nativo si el valor no cumple la regla. En móvil, el teclado o el autocompletado a veces introducen espacios o formatos que el patrón rechaza. Comprueba si el navegador muestra el globo de validación nativo al pulsar Enviar.
CORS y envío por AJAX
Si el formulario no hace POST clásico sino fetch o XMLHttpRequest, el servidor debe responder con cabeceras Access-Control-Allow-Origin adecuadas. Un error de CORS en consola indica que la petición nunca llegó al backend de forma útil para el navegador, aunque el servidor haya registrado algo en logs.
Proveedor de formularios: límites, verificación o dominio
Servicios como Formspree, Getform, Basin o Netlify Forms exigen a veces verificar el dominio, activar el formulario en el panel o respetar límites del plan gratuito. Un formulario no verificado puede aceptar envíos de prueba desde el panel pero rechitarlos desde tu web. Revisa el estado del proyecto y los correos de alerta del proveedor.
Correo enviado pero no visto
El envío puede ser correcto y el fallo estar en la entrega de correo: mensaje en spam, regla del buzón que archiva automáticamente, buzón equivocado configurado en el panel, o SPF/DKIM del dominio remitente incompletos. Siempre contrasta el panel del servicio de formularios con la bandeja de entrada real.
reCAPTCHA, honeypot o reglas anti-spam
Campos ocultos tipo honeypot mal implementados (visibles para lectores de pantalla o rellenados por autocompletado) marcan envíos legítimos como spam. reCAPTCHA v3 con umbral alto o claves de sitio de entorno de prueba en producción bloquean envíos sin mensaje claro. Cloudflare Turnstile y reglas WAF pueden devolver 403 al POST del formulario.
HTTPS, contenido mixto y redirecciones
Un formulario en página HTTPS que intenta enviar a HTTP puede ser bloqueado. Redirecciones en cadena en el action pueden convertir un POST en GET y perder el cuerpo del mensaje. Comprueba la URL final tras seguir redirecciones con las herramientas de red.
Checklist copiable
FORMULARIO QUE NO ENVÍA - CHECKLIST □ URL exacta de la página con el formulario □ Qué ocurre al pulsar Enviar (nada / error / gracias sin correo) □ Navegador y dispositivo donde falla (Chrome, Safari, móvil) □ Herramientas de desarrollo → Red: ¿sale petición al enviar? □ Código HTTP de la respuesta (200, 302, 403, 422, 500) □ action del <form>: URL completa y correcta □ method="post" (salvo documentación distinta del proveedor) □ name en cada campo que debe llegar al backend □ Consola: errores JavaScript o CORS □ Prueba en ventana privada sin extensiones □ Panel del proveedor (Formspree, Netlify, etc.): ¿aparece el envío? □ Buzón destino: spam, promociones, reglas de filtro □ Cambios recientes (deploy, DNS, Cloudflare, nuevo script) □ reCAPTCHA / honeypot: ¿activos y con claves de producción? □ Página de gracias o redirect configurado en el proveedor
Qué capturar
Para acortar el diagnóstico remoto, prepara evidencia concreta sin incluir datos personales de terceros ni secretos del backend.
Captura de la pestaña Red
Con las herramientas de desarrollo abiertas, envía el formulario una vez y captura la fila de la petición principal: URL solicitada, método, código de estado y, si es posible, la respuesta del servidor (mensaje de error JSON o HTML). Si no aparece ninguna petición, el bloqueo es anterior al envío: JavaScript, validación HTML o un botón que no es type="submit".
Fragmento del HTML del formulario
Comparte el bloque <form> con sus atributos y los nombres de los campos, censurando tokens ocultos o claves de sitio si las hay. Indica si el envío es nativo o gestionado por un script (y qué archivo lo controla).
Proveedor y configuración visible
Nombre del servicio (Formspree, Netlify Forms, endpoint propio en VPS, plugin de WordPress, etc.), correo de notificación configurado en el panel y si el dominio está verificado. Una captura del panel mostrando envíos recientes (con direcciones de visitantes censuradas) confirma si el problema es web o correo.
Consola del navegador
Errores en rojo al pulsar Enviar: CORS, clave de reCAPTCHA inválida, script no encontrado, violación de política de contenido. Copia el texto literal o captura la consola.
Contexto del cambio
Qué se modificó antes del fallo: nueva plantilla, cambio de URL del action, activación de Cloudflare, actualización de plugin, migración de hosting. La fecha aproximada ayuda a correlacionar con deploys o caducidad de certificados.
Prueba cruzada
Indica si el mismo formulario falla en todos los navegadores o solo en uno, y si en red móvil se comporta distinto. Eso descarta rápido extensiones locales o DNS split en redes corporativas.
No envíes contraseñas del panel, API keys completas ni exports de .env. Si hace falta acceso al hosting o al repositorio, se acuerda por correo con alcance escrito.
Cuándo escalar
Puedes seguir solo si el fallo es claramente localizable y reversible: action mal escrito, campo sin name, JavaScript de validación que puedes desactivar y probar, o correo en spam que recuperas con una regla de buzón. Documenta el cambio y vuelve a probar desde un dispositivo que no hayas usado para las pruebas.
Conviene escalar cuando:
- La petición devuelve 403, 500 o timeout y no tienes acceso al servidor o al panel del proveedor para leer logs.
- El formulario depende de un backend propio (API, serverless, webhook) y la respuesta indica error de autenticación o de base de datos.
- Cloudflare u otro WAF bloquea el POST y no sabes qué regla crear sin afectar al resto del sitio.
- Tras corregir action y scripts, el panel del proveedor registra envíos pero ningún buzón recibe notificaciones y ya revisaste SPF, DKIM y destinatarios.
- El sitio es tienda o captación crítica y cada hora sin formulario tiene impacto directo en pedidos o leads.
- Hay integración con CRM, Zapier o Make y el fallo está en la cadena posterior al primer endpoint, no en el HTML visible.
Al escribir por el formulario de contacto, elige la categoría Web o despliegue e incluye la checklist marcada, las capturas de red y consola, y la URL afectada. Con eso suele bastar para proponer un arreglo concreto (corregir plantilla, ajustar regla en Cloudflare, reconfigurar notificaciones o simplificar el envío a POST nativo) sin idas y vueltas.
Si el formulario es el de fixeador u otro sitio estático similar, recuerda que un action apuntando al endpoint correcto del proveedor y un redirect a página de gracias en el mismo dominio es el patrón más robusto. La complejidad solo se justifica cuando necesitas lógica de negocio en servidor; hasta entonces, menos capas significa menos puntos de rotura.