Para probar Mailgun webhooks en localhost, exponga su manejador local con npx portpreview PORT, configure el punto final HTTPS resultante para los tipos de eventos necesarios Mailgun, y verifique la marca de tiempo, token y HMAC-SHA256 de la carga útil antes de aceptar el evento.
¿Qué Mailgun webhooks report
Mailgun envía un HTTP o HTTPS POST con una carga útil JSON cuando se produce un evento configurado. Tipos de evento actuales incluyen accepted, delivered, temporary_fail, permanent_fail, opened, clicked, quejas de spam, y suscripciones. Los eventos dependientes de seguimiento sólo aparecen cuando el seguimiento correspondiente está habilitado.
Un cuerpo actual Mailgun Send Webhook tiene un signature objeto event-data. Los datos del evento contienen campos como event, id, timestamp, encabezados de mensajes, información de destinatarios, etiquetas y detalles de entrega, dependiendo del tipo de evento. Código contra campos documentados y tolerar propiedades opcionales ausentes. Mailguns official ejemplos de carga útil son los mejores accesorios para las pruebas de contrato.
No confunda un Mailgun Send webhook con Mailgun Alertas. Las alertas utilizan una clave de firma diferente y firman todo el cuerpo POST en un X-Sign Cabeza. Esta guía cubre Enviar webhooks: los campos de firma en la carga útil y la cuenta Webhook Signing Key.
1. Construir un punto final local Mailgun
A diferencia de los esquemas que firman el cuerpo JSON crudo, el cálculo documentado de Mailgun Send utiliza el timetamp y token del objeto firma. Por lo tanto, es apropiado el análisis estándar de JSON. El siguiente manipulador Express verifica HMAC, realiza un cheque de repetición y acepta duramente el evento.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.use(express.json({ limit: '1mb' }));
function verifyMailgunSignature({ timestamp, token, signature }) {
if (!timestamp || !token || !signature) return false;
const expected = crypto
.createHmac('sha256', process.env.MAILGUN_WEBHOOK_SIGNING_KEY)
.update(String(timestamp) + String(token))
.digest('hex');
const expectedBytes = Buffer.from(expected, 'hex');
const actualBytes = Buffer.from(String(signature), 'hex');
return expectedBytes.length === actualBytes.length &&
crypto.timingSafeEqual(expectedBytes, actualBytes);
}
app.post('/webhooks/mailgun', async (req, res) => {
const signing = req.body?.signature;
const event = req.body?.['event-data'];
if (!signing || !event || !verifyMailgunSignature(signing)) {
return res.status(406).send('invalid webhook');
}
const ageSeconds = Math.abs(Date.now() / 1000 - Number(signing.timestamp));
if (!Number.isFinite(ageSeconds) || ageSeconds > 15 * 60) {
return res.status(406).send('stale webhook');
}
await acceptOnce({
eventId: event.id,
replayToken: signing.token,
payload: event,
});
return res.sendStatus(200);
});
app.listen(3000);
La ventana de 15 minutos es una política de aplicación, no un valor Mailgun-mandated. Mailgun recomienda comprobar que el timetamp no está muy lejos del tiempo actual, pero advierte contra ser demasiado agresivo porque la entrega puede retrasarse. Elija una ventana que se ajuste a sus requisitos de búsqueda y recuperación de incidentes, vigile los rechazos legítimos y ajuste deliberadamente.
Guarde el Webhook Signing Key en un gestor secreto o variable ambiente, nunca en el control de fuente. Mailgun guía de seguridad webhooks define el cálculo exacto: timetamp concatenate y token sin separador, compute HMAC-SHA256 utilizando el Webhook Signing Key, y compare el hexadecimal digest con signature.
2. Expose localhost over HTTPS
Con la aplicación escuchando en el puerto 3000, ejecutar:
npx portpreview 3000
Apéndice la ruta local al origen público HTTPS. Por ejemplo:
https://example.portpreview.dev/webhooks/mailgun
Deje tanto la aplicación como el túnel que se ejecuta durante la prueba. Mailgun necesita una URL accesible públicamente; localhost, una dirección LAN privada, y un certificado de desarrollo autofirmado no son destinos remotos adecuados. PortPreview termina HTTPS público y envía la solicitud a su puerto local.
3. Configure Mailgun URLs del evento
Mailgun es compatible con la configuración de webhook de nivel de cuenta y de dominio. Los puntos finales de nivel de cuenta pueden recibir eventos en todos los dominios y subcuentas heredadas; los puntos finales de dominio se aplican sólo a ese dominio. Cada tipo de evento se configura individualmente y puede tener hasta tres URLs. Seleccione el alcance más estrecho que coincida con su aplicación.
- Abra el área Webhooks para la cuenta prevista o el dominio de envío.
- Elija un tipo de evento, como
deliveredopermanent_fail. - Añadir el punto final completo PortPreview HTTPS.
- Repita por cada tipo de evento que soporta su manejador.
- Enviar una prueba o mensaje real e inspeccionar los registros de solicitud y aplicación locales.
Mailgun deduplica la misma URL para el mismo evento cuando se configura tanto a nivel de cuenta como de dominio, pero diferentes URLs pueden recibir cada copia. La herencia parent-account también puede causar entregas a múltiples puntos finales distintos. Examen del funcionario reglas de configuración antes de atribuir cada entrega adicional a los camiones.
Cómo funciona la verificación de firmas Mailgun
El signature objeto contiene:
timestamp: Unix tiempo en segundos.token: una cadena de 50 caracteres generada al azar.signature: un hexadecimal HMAC digest.parent-signature: opcionalmente presente para un evento de una subcuenta, permitiendo validación contra la relación de la cuenta primaria descrita por Mailgun.
Para la firma de la cuenta normal, calcula HMAC-SHA256(signingKey, timestamp + token). No hay separador y el event-data JSON no es parte de este cálculo documentado Mailgun Send. Compare los bytes decodificados con una función segura de tiempo después de comprobar longitudes iguales. Una llanura === la comparación es más simple, pero una comparación segura de tiempo es la falta de producción más segura.
Un auténtico HMAC demuestra que una parte que sostiene la clave de firma produjo la firma. No prueba que esta entrega no haya sido repetida. Mailgun recomienda específicamente cachear la ficha y rechazar una solicitud posterior con la misma ficha. Una verificación de tiempos limita cuánto tiempo una solicitud válida capturada sigue siendo útil. Use ambos controles: una limitación de token única para la repetición y una ventana de tiempo razonable para la frescura.
Deduplicar tanto las entregas como los efectos
Mantenga dos limitaciones de singularidad duraderas: una para el token de firma y otra para el Mailgun event-data.id. El token captura una repetición de entrega firmada idéntica. El ID de evento protege la lógica empresarial si el mismo evento aparece en otro contexto de entrega válido. Namespace tanto por proveedor como por cuenta o entorno.
async function acceptOnce({ eventId, replayToken, payload }) {
await db.transaction(async (tx) => {
const tokenWasNew = await tx.webhookTokens.insertIfAbsent({
provider: 'mailgun',
token: replayToken,
});
if (!tokenWasNew) return;
const eventWasNew = await tx.webhookEvents.insertIfAbsent({
provider: 'mailgun',
eventId,
receivedAt: new Date(),
});
if (!eventWasNew) return;
await tx.jobs.enqueue({
type: 'process-mailgun-event',
payload,
});
});
}
Retrocede las operaciones insert-if-absent con índices únicos de la base de datos; una lectura seguida de un inserto es propensa a la raza bajo entregas simultáneas. Presentar los registros de despido y el trabajo de cola atómico. A continuación, reconocer rápidamente y dejar que un trabajador actualizar mensaje estado, desencadenar alertas, o sincronizar un CRM. Ver el guía de retry e idempotency para alternativas cuando la base de datos de colas y negocios no puede compartir una transacción.
Mailgun códigos de respuesta y comportamiento de reingreso
Mailgun la documentación actual Enviar webhook da tres resultados importantes:
- 200 Éxito: Mailgun trata el Webhook POST como exitoso y no lo retrata.
- 406 No aceptable: Mailgun trata al POST como rechazado y no lo retrata.
- Cualquier otro código: para webhooks aparte de las notificaciones de entrega, Mailgun se registra más de ocho horas a 5 minutos, 10 minutos, 15 minutos, 1 hora, 2 horas y 4 horas.
La excepción de la notificación de entrega importa: no prometer que cada tipo de evento siga el calendario de reingreso general. Compruebe lo último documentación de los registros automáticos cuando las garantías de entrega afectan su diseño.
Utilice 406 sólo para una solicitud que rechaza intencionadamente permanentemente, como una firma inválida o una repetición de política exterior. Utilice 500 o 503 para bases de datos transitorias y fallas de cola para que los tipos de gancho web elegibles puedan volver a entrar. Retorno 200 sólo después de la aceptación duradera. Volver a 200 mientras se inicia un trabajo de fondo sin seguimiento puede perder el evento si el proceso sale.
Troubleshooting Mailgun webhooks locally
El HMAC computado nunca coincide
Confirme que está usando el Webhook Signing Key, no una clave de API, contraseña SMTP, o la clave de firma de Alertas. Concatenar el tempo y token del objeto de firma sin delimitador. Produce un hexadecimal de minúscula SHA-256 digest. También verifique que su marco no ha renombrado el hipnotizado event-data propiedad; notación entre corchetes evita ese error.
El manejador recibe campos de forma en lugar de JSON actual
Compruebe qué función Mailgun y versión de endpoint generó la solicitud. No aplique un tutorial de payload legado ciegamente a un Webhook de Send actual. Tipo de contenido de registro, nombres de campo de alto nivel y duración del cuerpo en desarrollo sin contenidos de mensajes de registro o secretos, luego implementar el contrato documentado para su cuenta e integración.
Mailgun sigue reintentando
Inspeccione el estado real enviado por el cable. Una excepción después de la confirmación de la base de datos puede convertir la respuesta en 500, causando otro intento. Es por eso que el ID del evento y los insertos de token deben ser únicos y duraderos. Si una solicitud es inválida permanentemente, devolver 406; si el fallo es transitorio, fijar el servicio y permitir que el comportamiento de reingreso funcione.
Ningún evento alcanza localhost
Confirme la URL se adjunta a la cuenta o dominio correctos y al tipo de evento exacto que se está produciendo. A delivered URL no recibirá opened eventos. Compruebe que el proceso local y el túnel todavía están activos y que la ruta configurada es /webhooks/mailgun. Seguir el local webhook guía de depuración separar la configuración del proveedor de errores de enrutamiento y aplicación.
Lista de verificación de seguridad
- Verificar HMAC antes de confiar o registrar
event-data. - Mantener la clave de firma en una tienda secreta y rotarla a través de un despliegue controlado; nunca exponerla en el código lado cliente.
- Use la comparación de la digestión segura de tiempo, una política de tiempos y una limitación única duradera en la ficha.
- Validar el tipo de evento y los campos requeridos antes de solicitar. Trate de direcciones, sujetos, URLs de almacenamiento y variables de usuario como datos sensibles.
- Aceptar sólo POST, tamaño del cuerpo de la tapa, utilizar HTTPS, y fallas de límite de velocidad sin bloquear registros legítimos Mailgun.
- No exponga puntos finales de administración o depuración locales no relacionados a través del origen público temporal.
- Cuando termina la prueba, eliminar la URL temporal y configurar el punto final de producción estable.
Mailgun también documenta un certificado de cliente TLS opcional en solicitudes webhook cuando su servidor receptor tiene TLS válido. Esto puede proporcionar validación de nivel de transporte, pero no reemplaza verificación HMAC de carga útil, controles de reproducción y autorización de aplicación. Controles de capas según tu modelo de amenaza.
Pruebas de aceptación de la producción
- Entrega una fijación firmada válida y confirma un evento duradero más una respuesta de 200.
- Cambia la ficha sin cambiar la firma y confirma un 406 sin necesidad de escribir un evento.
- Reproduce el cuerpo exacto válido y no confirme ningún segundo trabajo o efecto secundario.
- Enviar una firma válida con un timetamp fuera de la ventana configurada y verificar el rechazo previsto.
- Forzar un error de base de datos temporal, confirmar una respuesta no-200/non-406, luego restaurar la base de datos y verificar una aceptación exitosa.
- Ejerce cada tipo de evento Mailgun configurado porque los campos de carga y las expectativas de reingreso difieren.
Una vez que esas pruebas pasan, utilice el mismo camino de verificación y deduplicación en la producción. Para una explicación independiente del proveedor de comparación HMAC y manejo secreto, lea la guía de verificación de firmas webhook.
