Has desplegado en GitHub Pages, Netlify o Cloudflare Pages. Añades un <form>, un visitante pulsa Enviar, y el envío no llega a ninguna parte porque no tienes un servidor que lo reciba. Ese es el problema: un formulario de contacto necesita un destino para sus peticiones POST y un alojamiento estático no lo proporciona.
Tienes tres opciones realistas: un mailto: , una función serverless con una API de email o un endpoint externo. Los veremos por orden de esfuerzo y explicaremos dónde falla cada uno.
Opción 1: enlace mailto:
La opción sin esfuerzo es prescindir del formulario:
<a href="mailto:[email protected]">Email me</a>No cuesta nada y falla de tres maneras:
- Los recolectores de spam leen tu HTML. Los bots rastrean las direcciones publicadas en texto y el spam empieza en pocos días. (Hemos escrito sobre qué funciona contra el rastreo por separado.)
- Depende del cliente de correo del visitante.
mailto:abre el programa predeterminado. En un portátil de trabajo o móvil suele ser Outlook sin configurar o nada. El visitante ve una pestaña vacía y abandona. - No tienes estructura. Sin campos obligatorios, asunto elegido ni formato uniforme.
<form action="mailto:...">es aún peor: los navegadores lo gestionan de forma desigual y algunos no lo admiten.
Conclusión: válido para una página personal donde no importe perder mensajes. No es un formulario de contacto.
Opción 2: función serverless y API de email
La opción manual: funciones serverless reciben POST y una API (Resend, Postmark, SES u otra) lo entrega a tu correo. El código es breve:
// netlify/functions/contact.js (or a Vercel function, or a Worker)
export default async function handler(req) {
const { name, email, message } = await req.json();
if (!email || !message) {
return new Response("Missing fields", { status: 400 });
}
// Every email API looks roughly like this
await fetch("https://api.email-provider.example/send", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.EMAIL_API_KEY}` },
body: JSON.stringify({
to: "[email protected]",
subject: `Contact form: ${name || "someone"}`,
reply_to: email,
text: message,
}),
});
return Response.json({ ok: true });
}Funciona y es razonable si ya utilizas funciones. Pero calcula su coste real:
- Una función que programar, desplegar y mantener cuando cambian los entornos de ejecución.
- Una cuenta con un proveedor de email, normalmente con verificación de dominio para una entrega fiable.
- Claves API que crear, guardar como variables de entorno y renovar.
- El spam corre de tu cuenta. El primer bot que descubra el endpoint lo utilizará y tendrás que programar límites y trampas.
- Todo eso se repite en cada sitio que publicas. El cuarto proyecto también necesita su propia función, clave y configuración.
Fíjate en lo ocurrido: tu sitio «estático y sin servidor» ahora tiene un servidor. Pequeño, pero debes mantenerlo.
Opción 3: endpoint de formulario
Un endpoint de formulario es una URL que acepta las peticiones POST de tu formulario y guarda los mensajes donde puedas verlos. Tu sitio sigue siendo estático; el servicio hace de servidor. Toda la integración cabe en un atributo:
<form data-mailcontact-form action="https://mailcontact.app/api/forms/your-form-id" method="POST">
<input type="text" name="name" placeholder="Your name" />
<input type="email" name="email" placeholder="[email protected]" required />
<input type="text" name="subject" placeholder="Subject" />
<textarea name="message" placeholder="What's on your mind?" required></textarea>
<button type="submit">Send</button>
<input name="company_website" type="text" tabindex="-1" autocomplete="off"
style="position:absolute;left:-10000px" aria-hidden="true" />
</form>Ya tienes un formulario funcional. Con el endpoint de mailcontact, la URL acepta datos de formulario estándar o JSON y exige un email y un message (el nombre y el asunto son opcionales), y responde {"ok": true}. CORS está abierto, así que funciona desde cualquier origen: GitHub Pages, Netlify, un bucket de S3 e incluso un archivo HTML local. Los envíos tienen límites por IP y cada mensaje llega a la bandeja de tu proyecto.
Para mantener a los visitantes en tu página después de enviar, utiliza el script alojado de MailContact en lugar del envío nativo. Gestiona la confirmación, los errores y la verificación automática e inserta la comprobación oculta cuando hace falta:
<!-- Add data-mailcontact-form to the form above. -->
<script defer src="https://mailcontact.app/forms/v1.js"
crossorigin="anonymous" referrerpolicy="no-referrer"></script>Es adecuado si quieres facilitar contacto sin mantener nada. Confías en un servicio en vez de tu código. Si necesitas lógica propia, adjuntos o CRM, programa la función. Para contacto estático, el endpoint gana en tiempo, mantenimiento y claves API (ninguna). La guía de sitios estáticos explica los detalles propios del alojamiento.
Recibir avisos y separar proyectos
Elijas lo que elijas, el objetivo es leer mensajes. Un formulario que entrega a una bandeja que nunca miras es un mailto: con pasos adicionales.
Aquí ayuda mailcontact: cada proyecto tiene una bandeja y un canal avisa cuando llega algo: Discord, Slack, email o un webhook. La misma bandeja también incluye una dirección de email real ([email protected]) que funciona al instante, sin configurar DNS ni servidores de correo, de modo que el formulario y el enlace «o escríbenos» llegan al mismo lugar.
Cuando publiques el siguiente proyecto, crea otra bandeja en vez de dirigir otro formulario a tu correo personal. Hemos escrito sobre por qué cada proyecto merece su bandeja. Separar los mensajes permite gestionar cinco proyectos sin multiplicar el ruido.