Seguridad y Buenas Prácticas
Enviar emails en desarrollo es fácil. Enviar emails en producción sin que te bloqueen, te marquen como spam o te suspendan la cuenta requiere seguir ciertas prácticas. Esta página cubre todo lo que necesitas verificar antes de poner tu integración con Resend en producción.
API Key Segura
Tu API key de Resend debe vivir exclusivamente en variables de entorno del servidor. Nunca en el código fuente, nunca en el repositorio de git y nunca expuesta al navegador.
# .env.local
RESEND_API_KEY=re_xxxxxxxxPuntos clave:
- Sin prefijo
NEXT_PUBLIC_: las variables con ese prefijo se incluyen en el bundle del cliente. Si tu API key tiene ese prefijo, cualquiera puede verla en el código fuente de tu página. .env.localestá en.gitignore: NextJS incluye.env.localen.gitignorepor defecto. Verifica que no lo hayas removido accidentalmente.- Variables en tu hosting: en Vercel, configura
RESEND_API_KEYen Settings > Environment Variables. Nunca hagas deploy con la key hardcodeada en el código.
Verificar Firma de Webhooks
Si configuraste webhooks, verifica la firma de cada request que recibes. Sin verificación, cualquiera puede enviar requests falsos a tu endpoint. Revisa la guía completa en la página de webhooks.
Rate Limiting
Resend permite 2 requests por segundo en la mayoría de planes. Si tu aplicación envía emails en ráfagas (por ejemplo, al importar usuarios o enviar notificaciones masivas), necesitas implementar una cola o al menos un mecanismo de retry con backoff:
import { resend } from '@/lib/resend'
interface EmailData {
from: string
to: string[]
subject: string
html: string
}
async function enviarConRetry(emailData: EmailData, intentos = 3) {
for (let i = 0; i < intentos; i++) {
const { data, error } = await resend.emails.send(emailData)
if (!error) return { data }
if (error.message.includes('rate_limit')) {
// Esperar con backoff exponencial: 1s, 2s, 3s
await new Promise(r => setTimeout(r, 1000 * (i + 1)))
continue
}
// Error que no es de rate limit -- no reintentar
return { error }
}
return { error: { message: 'Se agotaron los intentos' } }
}Para volúmenes grandes (miles de emails), considera usar una cola de trabajo como BullMQ o Inngest en vez de enviar todo directamente.
Dominio Verificado
En desarrollo puedes enviar desde tu@resend.dev, pero en producción debes usar un dominio verificado. Esto significa:
- SPF (Sender Policy Framework): indica qué servidores pueden enviar emails en nombre de tu dominio
- DKIM (DomainKeys Identified Mail): firma criptográfica que verifica que el email no fue alterado en tránsito
- DMARC (Domain-based Message Authentication, Reporting and Conformance): política que le dice a los servidores de destino qué hacer con emails que fallan SPF o DKIM
Resend configura SPF y DKIM automáticamente al verificar tu dominio. Para DMARC, necesitas agregar un registro DNS manualmente. Como mínimo, configura:
_dmarc.tudominio.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@tudominio.com"La política p=quarantine le dice a los servidores de destino que envíen a spam los emails que no pasen la verificación. Es un buen punto de partida antes de escalar a p=reject.
Manejo de Bounces
Un bounce (rebote) ocurre cuando un email no puede ser entregado. Las causas más comunes son: dirección que no existe, buzón lleno o dominio inactivo.
Lo que debes hacer:
- Configura un webhook para el evento
email.bounced - Cuando recibas un bounce, marca esa dirección como inválida en tu base de datos
- No vuelvas a enviar emails a direcciones que rebotaron
case 'email.bounced':
await db.contacto.update({
where: { email: payload.data.to },
data: { emailValido: false }
})
breakBounce rate alto = cuenta suspendida
Si tu bounce rate (tasa de rebotes) supera el 5%, Resend puede suspender tu cuenta. Esto protege la reputación de sus servidores de envío. Limpia tu lista de contactos regularmente y nunca envíes a listas compradas o sin verificar.
Manejo de Complaints
Un complaint (queja) ocurre cuando un usuario marca tu email como spam. Es la señal más fuerte de que no quieren recibir tus emails.
Lo que debes hacer:
- Configura un webhook para el evento
email.complained - Desuscribe automáticamente al usuario -- no le envíes más emails
- No intentes revertir la desuscripción
case 'email.complained':
await db.contacto.update({
where: { email: payload.data.to },
data: { desuscrito: true, motivoDesuscripcion: 'complaint' }
})
breakMonitoreo
Revisa el dashboard de Resend regularmente para monitorear:
- Deliverability (tasa de entrega): debería estar por encima del 95%
- Bounce rate (tasa de rebotes): debería estar por debajo del 2%, nunca superior al 5%
- Complaint rate (tasa de quejas): debería estar por debajo del 0.1%
Si alguna métrica se sale de rango, investiga inmediatamente. Un problema de deliverability puede escalar rápido y afectar todos tus envíos.
Puedes usar herramientas como datahogo.com/tools/headers-check para verificar que tus emails salen con los headers correctos en producción: SPF, DKIM, DMARC y cualquier otro header de autenticación.
Checklist de Producción
Antes de hacer deploy de tu integración con Resend, verifica cada punto:
- Dominio verificado con SPF, DKIM y DMARC configurados
- API key almacenada en variables de entorno del servidor (sin
NEXT_PUBLIC_) - Webhook configurado con verificación de firma (Svix)
- Manejo de bounces implementado -- marcar direcciones inválidas
- Manejo de complaints implementado -- desuscribir automáticamente
- Templates de email probados en Gmail, Outlook y Apple Mail
- Rate limiting implementado si envías en volumen
- Logs de emails enviados para debugging y auditoría