Pour tester Mailgun webhooks sur localhost, exposez votre gestionnaire local avec npx portpreview PORT, configurer le paramètre HTTPS résultant pour les types d'événements requis Mailgun, et vérifier l'horodatage, le jeton et la signature HMAC-SHA256 de la charge utile avant d'accepter l'événement.
Ce que Mailgun webhooks rapport
Mailgun envoie un HTTP ou HTTPS POST avec une charge utile JSON lorsqu'un événement configuré se produit. Les types d'événements actuels comprennent : accepted, delivered, temporary_fail, permanent_fail, opened, clicked, les plaintes de spam et les désabonnements. Les événements dépendants du suivi n'apparaissent que lorsque le suivi correspondant est activé.
Un corps actuel Mailgun Send webhook a un signature objet à côté event-data. Les données de l'événement contiennent des champs tels que event, id, timestamp, en-têtes de message, informations sur le destinataire, étiquettes et détails de livraison, selon le type d'événement. Coder contre les champs documentés et tolérer l'absence de propriétés facultatives. Le fonctionnaire de Mailgun exemples de charge utile sont les meilleurs appareils pour les tests contractuels.
Ne confondez pas un webhook Mailgun Send avec Mailgun Alertes. Les alertes utilisent une autre clé de signature et signent l'ensemble du corps POST dans un X-Sign en-tête. Ce guide couvre Send webhooks: les champs de signature dans la charge utile et le compte Webhook Signing Key.
1. Construire un paramètre local Mailgun
Contrairement aux schémas qui signent le corps brut JSON, le calcul documenté de Mailgun Send utilise l'horodatage et le jeton de l'objet signature. L'analyse standard JSON est donc appropriée. Le gestionnaire Express suivant vérifie HMAC, effectue une vérification d'âge de replay et accepte durablement l'événement.
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 fenêtre de 15 minutes est une politique d'application, pas une valeur prescrite par Mailgun. Mailgun recommande de vérifier que l'horodatage n'est pas trop éloigné de l'heure actuelle, mais met en garde contre l'agressivité excessive, car la livraison peut être retardée. Choisissez une fenêtre qui correspond à vos exigences en attente et en récupération des incidents, surveillez les rejets légitimes et ajustez-la délibérément.
EntreposerClé de signature Webhookdans un gestionnaire secret ou une variable d'environnement, jamais dans le contrôle des sources.Gun postal's guide de sécurité des hooks web définit le calcul exact: timestamp concaténaté et jeton sans séparateur, calculer HMAC-SHA256 à l'aide du Webhook Signing Key, et comparer le digest hexadécimal avec signature.
2. Expose localhost sur HTTPS
Avec l'application écoute sur le port 3000, exécutez:
npx portpreview 3000
Ajouter la route locale à l'origine publique HTTPS. Par exemple:
https://example.portpreview.dev/webhooks/mailgun
Laisser tourner l'application et le tunnel pendant le test. Mailgun a besoin d'une URL accessible au public; localhost, une adresse LAN privée et un certificat de développement autosigné ne conviennent pas aux destinations distantes. PortPreview met fin au HTTPS public et transmet la demande à votre port local.
3. Configurer les URL des événements Mailgun
Mailgun prend en charge la configuration de webhook au niveau du compte et du domaine. Les paramètres de niveau de compte peuvent recevoir des événements entre domaines et sous-comptes hérités; les paramètres de niveau de domaine ne s'appliquent qu'à ce domaine. Chaque type d'événement est configuré individuellement et peut avoir jusqu'à trois URL. Sélectionnez la portée la plus étroite qui correspond à votre application.
- Ouvrez la zone Webhooks pour le compte prévu ou le domaine d'envoi.
- Choisissez un type d'événement, comme
deliveredoupermanent_fail. - Ajouter le paramètre HTTPS complet PortPreview.
- Répétez pour chaque type d'événement votre gestionnaire prend en charge.
- Envoyer un test ou un message réel et inspecter les journaux des demandes et des applications locales.
Mailgun dédouble la même URL pour le même événement lorsqu'elle est configurée aux niveaux de compte et de domaine, mais différentes URL peuvent recevoir une copie. L'héritage du compte parent peut aussi causer des livraisons à plusieurs paramètres distincts. Revoir le fonctionnaire règles de configuration avant d'attribuer chaque livraison supplémentaire aux rétries.
Comment fonctionne la vérification de la signature Mailgun
Les signature objet contient:
timestamp: Temps Unix en secondes.token: une chaîne de 50 caractères générée au hasard.signature: un digesteur HMAC hexadécimal.parent-signature: optionnellement présent pour un événement d'un sous-compte, permettant la validation par rapport à la relation de compte primaire décrite par Mailgun.
Pour la signature normale du compte, calculer HMAC-SHA256(signingKey, timestamp + token). Il n'y a pas de séparateur et le JSON event-data ne fait pas partie de ce calcul documenté Mailgun Send. Comparer les octets décodés avec une fonction de sécurité chronométrée après vérification des longueurs égales. Une plaine === La comparaison est plus simple, mais une comparaison à un moment sûr est la plus sûre par défaut de production.
Un HMAC authentique prouve qu'une partie détenant la clé de signature a produit la signature. Il ne prouve pas que cette livraison n'a pas été rejouée. Mailgun recommande expressément de mettre en cache le jeton et de rejeter une demande ultérieure avec le même jeton. Une vérification de l'âge limite la durée de validité d'une demande capturée. Utilisez les deux commandes : une contrainte unique pour le replay et une fenêtre de temps raisonnable pour la fraîcheur.
Dupliquer les livraisons et les effets
Conserver deux contraintes d'unicité durable: une pour le jeton de signature et une pour le Mailgun event-data.id. Le jeton prend un replay de livraison signé identique. L'identifiant d'événement protège la logique d'entreprise si le même événement apparaît dans un autre contexte de livraison valide. Espace de noms par fournisseur et compte ou environnement.
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,
});
});
}
Back les opérations insert-if-absent avec des index uniques de base de données; une lecture suivie d'un insert est course-prone sous les livraisons simultanées. Commettez les enregistrements de dedup et le travail de queue atomiquement. Puis reconnaissez rapidement et laissez un travailleur mettre à jour l'état du message, déclencher des alertes, ou synchroniser un CRM. Voir Guide d'urgence pour les options lorsque la base de données de la file d'attente et de l'entreprise ne peut pas partager une transaction.
Mailgun codes de réponse et comportement de réessayer
La documentation actuelle de Mailgun Send webhook donne trois résultats importants :
- 200 Succès : Mailgun traite le webhook POST comme un succès et ne réessaye pas.
- 406 Non acceptable : Mailgun traite le POST comme rejeté et ne le réessaye pas.
- Tout autre code: pour les webhooks autres que les notifications de livraison, Mailgun répond pendant huit heures à 5 minutes, 10 minutes, 15 minutes, 1 heure, 2 heures et 4 heures.
L'exception à la notification de livraison est importante : ne promets pas que chaque type d'événement suit le calendrier général de ré-essai. Vérifiez la dernière la documentation automatique des relevés lorsque les garanties de livraison affectent votre conception.
Utilisez 406 uniquement pour une demande que vous rejetez intentionnellement en permanence, comme une signature invalide ou une nouvelle politique extérieure. Utilisez 500 ou 503 pour les défaillances transitoires de la base de données et de la file d'attente afin que les types de webhook admissibles puissent réessayer. Retour 200 seulement après acceptation durable. Retourner 200 tout en commençant un travail de fond non suivi peut perdre l'événement si le processus quitte.
Dépannage Mailgun webhooks localement
Le HMAC calculé ne correspond jamais
Confirmez que vous utilisez la Webhook Signing Key, pas une clé API, un mot de passe SMTP ou une clé de signature d'alerte. Concaténez l'horodatage de l'objet signature et jeton sans délimiteur. Produire un digesteur hexadécimal SHA-256 minuscule. Vérifiez également que votre cadre n'a pas rebaptisé le trait d'union event-data propriété; la notation entre crochets évite cette erreur.
Le gestionnaire reçoit des champs de formulaire au lieu de JSON actuel
Vérifiez quelle fonction Mailgun et quelle version du paramètre ont généré la demande. N'appliquez pas aveuglément un tutoriel de charge utile à un webhook actuel. Type de contenu journal, noms de champs de haut niveau, et la longueur du corps en développement sans contenu de message journal ou secrets, puis implémenter le contrat documenté pour votre compte et intégration.
Mailgun continue de réessayer
Inspectez l'état réel envoyé sur le fil. Une exception après le commit de base de données peut transformer la réponse en 500, provoquant une autre tentative. C'est pourquoi l'identifiant d'événement et les inserts de jeton doivent être uniques et durables. Si une demande est définitivement invalide, retournez 406; si l'échec est transitoire, corrigez le service et laissez fonctionner le comportement de réessayer.
Aucun événement n'arrive localhost
Confirmer que l'URL est attachée au bon compte ou au bon domaine et au type exact d'événement produit. A delivered URL ne recevra pas opened les événements. Vérifiez que le processus local et le tunnel sont toujours actifs et que le chemin configuré est /webhooks/mailgun. Suivez le guide local de débogage webhook séparer la configuration du fournisseur des erreurs de routage et d'application.
Liste de contrôle pour la sécurité
- Vérifier l'AMAC avant de faire confiance ou d'enregistrer
event-data. - Garder la clé de signature dans un magasin secret et la faire tourner à travers un déploiement contrôlé; ne jamais l'exposer en code côté client.
- Utiliser une comparaison des digests sans risque, une politique d'horodatage et une contrainte unique durable sur le jeton.
- Valider le type d'événement et les champs requis avant de faire la demande. Traiter les adresses de destinataires, les sujets, les URL de stockage et les variables utilisateur comme des données sensibles.
- Accepter uniquement POST, la taille du corps du capuchon, utiliser HTTPS, et les défauts de limite de vitesse sans bloquer les relevés légitimes Mailgun.
- Ne pas exposer les paramètres d'administration ou de débogage locaux non liés par l'origine publique temporaire.
- Lorsque le test prend fin, supprimez l'URL temporaire et configurez le paramètre de production stable.
Mailgun documente également un certificat TLS client optionnel sur les demandes webhook lorsque votre serveur de réception a TLS valide. Cela peut fournir une validation du niveau de transport, mais il ne remplace pas la vérification de la charge utile, les contrôles de rejouage et l'autorisation d'application. Contrôle des couches selon votre modèle de menace.
Essais d'acceptation de la production
- Livrer un appareil signé valide et confirmer un événement durable plus une réponse de 200.
- Changez le jeton sans changer la signature et confirmez un 406 sans écrire d'événement.
- Rejouer le corps exact valide et confirmer aucun second travail ou effet secondaire.
- Envoyez une signature valide avec un horodatage en dehors de votre fenêtre configurée et vérifiez le rejet prévu.
- Forcer une erreur temporaire de base de données, confirmer une réponse non-200/non-406, puis restaurer la base de données et vérifier une acceptation réussie.
- Exercer chaque type d'événement configuré Mailgun parce que les champs de charge utile et les attentes de réessayer diffèrent.
Une fois ces tests réussis, utilisez le même chemin de vérification et de déduplication dans la production. Pour une explication indépendante du fournisseur de la comparaison HMAC et de la manipulation secrète, lire guide de vérification de la signature de webhook.
