El correo es de las piezas que más pereza dan en un proyecto: registros DNS, reputación del remitente, rebotes. Cloudflare cubre las dos direcciones: recibir (Email Routing, maduro y gratuito) y enviar (Email Sending, en beta en noviembre de 2026). En este capítulo monto las dos y cuento cuándo sigo usando un proveedor externo.
Para quién es y qué necesitas
Para quien quiere recibir correo en su dominio, procesarlo con código o enviar correos desde su aplicación.
Antes de empezar:
- Un dominio en Cloudflare con su DNS (capítulo 2).
- Para enviar desde un Worker con Email Sending: el plan de pago de Workers.
- Una dirección tuya (Gmail, por ejemplo) donde reenviar lo que llegue.
Al terminar tendrás direcciones en tu dominio que reenvían a tu correo, un Worker que convierte correos en notas, envío de correos desde la aplicación y los registros SPF, DKIM y DMARC para no acabar en spam.
Cómo encaja el email en la aplicación
flowchart LR
R[Remitente] -->|hola@midominio.dev| ER[Email Routing]
ER -->|reenvío| B[Tu bandeja personal]
ER -->|notas@midominio.dev| EW[Email Worker]
EW --> D1[(D1)]
API[Worker API] -->|env.EMAIL.send| ES[Email Sending]
ES --> U[Usuario]
Recibir correo con Email Routing
En Email › Email Routing, al activarlo Cloudflare añade los registros MX y SPF necesarios. Después:
- Añade una dirección de destino (tu Gmail, por ejemplo) y verifícala con el correo que te llega.
- Crea reglas:
hello@mydomain.dev → you@gmail.com. - Opcional: una regla catch-all para cualquier dirección del dominio.
Procesar correo entrante con un Email Worker
En lugar de reenviar, una regla puede mandar el correo a un Worker. Así convierto los correos a notes@mydomain.dev en notas de la aplicación:
npm install postal-mimeimport PostalMime from "postal-mime";
const ALLOWED = new Set(["you@gmail.com"]);
export async function incomingEmail( message: ForwardableEmailMessage, env: Env,) { if (!ALLOWED.has(message.from)) { message.setReject("Sender not allowed"); return; }
const email = await PostalMime.parse(message.raw); await env.DB .prepare( "INSERT INTO notes (user_id, title, content) VALUES (?, ?, ?)", ) .bind("u_1", email.subject ?? "No subject", email.text ?? "") .run();
// Also send me a copy. await message.forward("you@gmail.com");}export default { fetch: app.fetch, email: incomingEmail,} satisfies ExportedHandler<Env>;Después, en Email Routing, crea la regla notes@mydomain.dev → Send to a Worker → notes.
Enviar correo desde un Worker con Email Sending
Email Sending envía correos transaccionales (verificación de cuenta, avisos, facturas) desde tu Worker, con un binding. Al dar de alta el dominio en Compute › Email Service › Email Sending › Onboard Domain, Cloudflare crea los registros SPF, DKIM y DMARC y los MX para gestionar rebotes en el subdominio cf-bounce.
{ "send_email": [{ "name": "EMAIL", "remote": true }]}type Template = "welcome" | "reminder";
const TEMPLATES: Record<Template, { subject: string; text: string }> = { welcome: { subject: "Welcome to Notes", text: "Thanks for signing up. Your first note is waiting for you.", }, reminder: { subject: "Shall we write the first note?", text: "Your notebook is still empty. A title is all you need to start.", },};
export async function sendEmail( env: Env, to: string, template: Template,) { const { subject, text } = TEMPLATES[template]; const response = await env.EMAIL.send({ from: { name: "Notes", email: "hello@mydomain.dev" }, to, subject, text, html: `<p>${text}</p>`, }); return response.messageId;}Avisos para ti, gratis
Enviar a direcciones de destino verificadas de tu propia cuenta es gratis en todos los planes, aunque solo tengas Email Routing. Es perfecto para alertas internas (“ha fallado el cron”, “nuevo usuario de pago”) sin contratar nada.
Cuándo uso un proveedor externo
| Necesidad | Email Sending (beta) | Proveedor externo (Resend, Postmark, Amazon SES) |
|---|---|---|
| Correos transaccionales de una app pequeña | ✅ Sin otra cuenta ni claves | ✅ |
| Plantillas con editor, analítica de aperturas | Básico | ✅ |
| Volúmenes altos y reputación ya probada | En beta: comprobar límites | ✅ |
| Newsletter y listas de suscriptores | ❌ No es su función | Usa un proveedor de newsletter |
Con un proveedor externo, el envío es una llamada HTTP con su clave guardada como secreto:
export async function sendWithResend(env: Env, to: string) { const res = await fetch("https://api.resend.com/emails", { method: "POST", headers: { Authorization: `Bearer ${env.RESEND_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: "Notes <hello@mydomain.dev>", to: [to], subject: "Welcome to Notes", text: "Thanks for signing up.", }), }); if (!res.ok) throw new Error(`Resend responded ${res.status}`);}Que tus correos no acaben en spam
| Registro | Qué demuestra | Ejemplo |
|---|---|---|
SPF (TXT) |
Qué servidores pueden enviar por tu dominio | v=spf1 include:… ~all |
DKIM (TXT) |
Que el mensaje no se ha modificado y viene de ti | Clave pública que te da el proveedor |
DMARC (TXT en _dmarc) |
Qué hacer con lo que no pasa SPF/DKIM | v=DMARC1; p=quarantine; rua=mailto:… |
Lo que comprueba el servidor que recibe tu correo, en orden:
flowchart TD
E[Tu correo llega] --> S{SPF: ¿este servidor puede<br/>enviar por tu dominio?}
S --> D{DKIM: ¿la firma<br/>es de tu dominio?}
D --> M{DMARC: ¿qué hago<br/>si algo falla?}
M -->|Todo bien| B[Bandeja de entrada]
M -->|Falla| X[Spam o rechazo]
Cuánto cuesta
Cuánto cuesta al mes
Supuestos: Recibir correo en el dominio y enviar avisos internos a direcciones verificadas
| Concepto | Importe |
|---|---|
| Email RoutingGratis en todos los planes | 0,00 $ |
| Email WorkersSe cobran como invocaciones de Workers | 0,00 $ |
| Envíos a direcciones verificadasGratis | 0,00 $ |
| Total al mes | 0,00 $ |
Precios consultados el 3 de noviembre de 2026. Pueden cambiar.
El envío a usuarios con Email Sending requiere el plan de pago de Workers (5 $ al mes). En noviembre de 2026, en beta, la documentación no publica un precio por correo: compruébalo antes de depender de él.
Resumen
- Email Routing para recibir en tu dominio; Email Workers para procesar lo que llega.
- Email Sending para enviar desde el Worker, con SPF, DKIM y DMARC configurados solos.
- Envío desde una cola o un Workflow, no dentro de la petición: si falla, se reintenta sin que el usuario lo note.
- Siguiente capítulo: imágenes.
