Gestion des rebonds Mandrill : prise en charge dans le cœur mais (à ma connaissance) non documentée

Core dispose d’un point de terminaison Mandrill fonctionnel pour les rebonds - POST /webhooks/mandrill dans WebhooksController - qui traite les événements hard_bounce et soft_bounce, applique hard_bounce_score / soft_bounce_score et désactive les adresses dépassant le bounce_score_threshold. L’association repose sur l’en-tête message_id de X-MC-Metadata que Email::Sender ajoute lorsque DISCOURSE_SMTP_ADDRESS est exactement smtp.mandrillapp.com.

J’essayais de configurer cela, mais j’ai rencontré quelques problèmes que je jugeais utile de partager au cas où cela aiderait d’autres personnes. Je sais qu’il s’agit d’un cas d’usage de niche, car Mandrill a cessé d’offrir du « simple SMTP » il y a plus d’une décennie, et ne propose le service SMTP que comme un module additionnel au produit d’e-mails transactionnels de Mailchimp. Néanmoins, un de mes clients avait exactement cette configuration, et nous y voilà donc !

1. Ce n’est documenté nulle part que je puisse trouver. Le sujet sur le VERP n’en mentionne pas, ce qui est compréhensible, car le VERP n’est pas la méthode de fonctionnement de Mandrill : la documentation de Mailchimp elle-même indique qu’il gère les rebonds lui-même, même lorsque vous configurez un domaine Return-Path personnalisé, de sorte que les rebonds par message n’atteignent jamais un récepteur de messagerie auto-hébergé. Les webhooks du fournisseur sont le seul chemin, et il n’y a aucun indice évident vers eux pour quiconque commence par le sujet sur le VERP.

2. Le webhook ne peut pas réellement être créé. WebhooksController#mandrill renvoie 406 chaque fois que mandrill_authentication_key est vide. Ce comportement est délibéré - c’est la correction pour CVE-2026-26077 (Discourse 2025.12.2 / 2026.1.1 / 2026.2.0), qui a fermé la possibilité de forger des charges utiles de rebond non authentifiées sur les points de terminaison SendGrid, Mailjet, Mandrill, Postmark, SparkPost et Mailpace. Il ne s’agit donc pas d’une demande pour assouplir cette restriction ; le problème est que la correction a laissé Mandrill sans moyen de démarrer.

Mandrill valide un nouveau webhook avec un POST (et non le HEAD pour lequel mandrill_head avait été écrit), voit le 406 et refuse d’enregistrer le webhook - si bien que la clé dont vous avez besoin n’est jamais visible. Le chemin d’API échoue de la même manière :

{"status":"error","code":-98,"name":"ValidationError","message":"Unable to validate webhook URL"}

Journal d’origine pour une tentative via l’interface utilisateur :

"POST /webhooks/mandrill HTTP/2.0" "Mandrill-Webhook/1.0" 406

Les autres fournisseurs ont des jetons au niveau du compte, ce qui permet à un administrateur de définir un secret arbitraire avant de créer le webhook. La clé de Mandrill est émise par webhook, seulement après l’enregistrement du webhook - c’est donc un dilemme.

Je suis presque certain que le flux pourrait être corrigé, peut-être en répondant 200 à la première vérification (qui n’a pas de charge utile mandrill_events), en permettant la création sur Mandrill, puis en copiant le secret manuellement dans Discourse, et nous aurons terminé. Je suis heureux d’ouvrir une PR si cela est acceptable.

3. Solution de contournement, si quelqu’un en a besoin aujourd’hui. Créez le webhook contre une URL qui renvoie déjà 200, avec aucun déclencheur (les charges utiles de rebond contiennent des adresses de destinataires, ne pointez donc jamais un webhook déclenché vers un espace réservé), puis installez la clé et déplacez-la :

# 1. créer contre un espace réservé, sans événements
curl -sS -X POST https://mandrillapp.com/api/1.0/webhooks/add.json \
  -H 'Content-Type: application/json' \
  -d '{"key":"<api-key>","url":"https://httpbin.org/status/200","description":"Discourse bounce handling","events":[]}'

# 2. mettre la clé d'authentification retournée dans le paramètre de site mandrill_authentication_key

# 3. rediriger vers le forum et activer les déclencheurs - cela passe maintenant la validation
curl -sS -X POST https://mandrillapp.com/api/1.0/webhooks/update.json \
  -H 'Content-Type: application/json' \
  -d '{"key":"<api-key>","id":<id>,"url":"https://<forum>/webhooks/mandrill","description":"Discourse bounce handling","events":["hard_bounce","soft_bounce"]}'

Testé sur 2026.9.0-latest.