核心代码中已经有一个可用的 Mandrill 退信端点——位于 WebhooksController 中的 POST /webhooks/mandrill——它会处理 hard_bounce 和 soft_bounce 事件,应用 hard_bounce_score / soft_bounce_score,并在超过 bounce_score_threshold 时禁用相关地址。匹配依赖于 Email::Sender 在 DISCOURSE_SMTP_ADDRESS 恰好为 smtp.mandrillapp.com 时添加的 X-MC-Metadata 中的 message_id 请求头。
我在尝试配置此功能时遇到了一些问题,认为值得分享出来,也许能帮助其他人。我知道这是一个小众的使用场景,因为 Mandrill 在十多年前就停止了“纯 SMTP”服务,直到最近才将 SMTP 服务作为 Mailchimp 事务性邮件产品的附加功能提供。尽管如此,我的一位客户恰好拥有这样的配置,所以我们就在这里了!
1. 我找不到任何地方有相关文档。 VERP 主题中并未提及此功能,这是合理的,因为 VERP 并不是 Mandrill 的工作方式:Mailchimp 自己的文档指出,即使你配置了自定义的 Return-Path 域名,它也会自行处理退信,因此单条邮件的退信永远不会到达自托管的邮件接收器。提供商 Webhook 是唯一的路径,而对于从 VERP 主题 开始配置的人来说,并没有明显的指引指向 Webhook。
2. 实际上无法创建该 Webhook。 只要 mandrill_authentication_key 为空,WebhooksController#mandrill 就会返回 406。这种行为是故意的——它是针对 CVE-2026-26077(Discourse 2025.12.2 / 2026.1.1 / 2026.2.0)的修复,该漏洞允许未经验证的攻击者在 SendGrid、Mailjet、Mandrill、Postmark、SparkPost 和 Mailpace 端点伪造退信负载。因此,这并非要求放宽限制;而是该修复导致 Mandrill 无法完成初始引导(bootstrap)。
Mandrill 会通过 POST 请求(而不是 mandrill_head 所针对的 HEAD 请求)来验证新的 Webhook,看到 406 后拒绝保存该 Webhook——因此你所需的密钥永远无法查看。API 路径也遇到完全相同的失败:
{"status":"error","code":-98,"name":"ValidationError","message":"Unable to validate webhook URL"}
通过 UI 尝试时的源日志:
"POST /webhooks/mandrill HTTP/2.0" "Mandrill-Webhook/1.0" 406
其他提供商拥有账户级别的令牌,因此管理员可以在创建 Webhook 之前 设置任意密钥。而 Mandrill 的密钥是按 Webhook 发放的,只有在 Webhook 保存后才会生成——因此这是一个死循环(Catch-22)。
我相当确定可以通过对首次验证(没有 mandrill_events 负载)返回 200 来修复此流程,从而允许在 Mandrill 上创建 Webhook,然后手动将密钥复制到 Discourse,问题即可解决。如果这样做是可以接受的,我很乐意提交一个 PR。
3. 如果现在就需要,这是一个变通方案。 针对一个已经返回 200 的 URL 创建 Webhook,且不设置任何触发器(退信负载包含收件人地址,因此切勿将已触发事件的 Webhook 指向占位符),然后在 Discourse 中安装该密钥,完成后移动 Webhook URL:
# 1. 针对占位符创建,不设置事件
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. 将返回的 auth_key 放入 mandrill_authentication_key 站点设置中
# 3. 重新指向论坛并启用触发器 - 现在可以通过验证
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"]}'
已在 2026.9.0-latest 上测试。