Core 已经有一个可用的 Mandrill 退信(bounce)端点 —— 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”服务,现在仅提供作为 Mailchimp 事务性邮件产品的附加组件的 SMTP 服务。尽管如此,我的一位客户恰好有这种配置,所以我们才在这里讨论!
1. 我找不到任何地方有文档说明。 VERP 主题中没有提到它,这是合理的,因为 VERP 并不是 Mandrill 的工作方式:Mailchimp 自己的文档说,即使你配置了自定义的 Return-Path 域,它也会自行处理退信,因此单条消息的退信永远不会到达自托管的邮件接收器。提供商 Webhook 是唯一的路径,对于从 VERP 主题 开始的人来说,并没有明显的指向它的指针。
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 在保存后才会签发的 —— 所以这是一个死循环(Catch-22)。
我相当确定可以通过对第一次验证(没有 mandrill_events 负载)响应 200 来修复此流程,允许在 Mandrill 上创建,然后可以将密钥手动复制到 Discourse,问题就解决了。如果这样可以接受,我很乐意提交一个 PR。
3. 如果有人今天需要,这是一个变通方法。 针对一个已经返回 200 的 URL 创建 Webhook,不设置任何触发器(退信负载包含收件人地址,因此永远不要将触发器 Webhook 指向占位符),然后安装密钥并移动它:
# 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 上测试。