Mandrill 바운스 처리: 코어에서 지원되지만 (AFAICT) 문서화되지 않음

Core에는 작동하는 Mandrill 바운스 엔드포인트가 있습니다. WebhooksControllerPOST /webhooks/mandrill로, hard_bouncesoft_bounce 이벤트를 처리하고 hard_bounce_score / soft_bounce_score를 적용하며, bounce_score_threshold를 초과한 주소를 비활성화합니다. 매칭은 Email::SenderDISCOURSE_SMTP_ADDRESS가 정확히 smtp.mandrillapp.com일 때 추가하는 X-MC-Metadata 헤더의 message_id에 의존합니다.

이것을 설정하려다 몇 가지 문제를 겪었는데, 다른 분들에게 도움이 될 수 있어 공유하고자 합니다. Mandrill이 10년 넘게 '단순 SMTP’를 중단했고, 이제야 Mailchimp의 트랜잭션 이메일 제품 추가 기능으로만 SMTP 서비스를 제공하므로, 이는 니치한 사용 사례임을 알고 있습니다. 그럼에도 불구하고 제 고객 중 한 명이 정확히 이 구성을 사용하고 있어 이렇게 글을 올립니다!

1. 제가 찾을 수 있는 어디에도 문서화되어 있지 않습니다. VERP 토픽에서는 이에 대해 언급하지 않으며, 이는 합리적입니다. Mandrill은 VERP 방식으로 작동하지 않기 때문입니다. Mailchimp의 자체 문서에는 커스텀 Return-Path 도메인을 구성하더라도 Mailchimp가 바운스를 자체적으로 처리한다고 명시되어 있어, 메시지별 바운스가 자체 호스팅된 메일 수신기에 도달하지 않습니다. 프로바이더 웹훅이 유일한 경로이며, VERP 토픽에서 시작하는 사용자에게는 웹훅에 대한 명확한 가리키기가 없습니다.

2. 웹훅을 실제로 생성할 수 없습니다. WebhooksController#mandrillmandrill_authentication_key가 비어 있을 때 항상 406을 반환합니다. 이 동작은 의도적인 것이며, SendGrid, Mailjet, Mandrill, Postmark, SparkPost 및 Mailpace 엔드포인트 전반에서 인증되지 않은 바운스 페이로드 위조를 차단한 CVE-2026-26077 (Discourse 2025.12.2 / 2026.1.1 / 2026.2.0)에 대한 수정입니다. 따라서 이를 완화해 달라는 요청이 아니라, 수정 과정에서 Mandrill의 부트스트랩 방법이 사라진 것입니다.

Mandrill은 새 웹훅을 POST로 검증합니다(mandrill_head가 작성된 HEAD가 아님), 406을 확인하고 웹훅 저장을 거부합니다. 따라서 필요한 키를 볼 수 없습니다. 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

다른 프로바이더에는 계정 수준의 토큰이 있어 관리자가 웹훅 생성 이전에 임의의 시크릿을 설정할 수 있습니다. Mandrill의 키는 웹훅이 저장된 후에만 웹훅별로 발급되므로, 이는 Catch-22 상황입니다.

첫 번째 검증(mandrill_events 페이로드가 없음)에 200으로 응답하여 Mandrill에서 생성을 허용하고, 그 후 시크릿을 Discourse에 수동으로 복사하여 해결할 수 있을 것이라고 확신합니다. 허용된다면 PR을 열어도 좋습니다.

3. 오늘 당장 필요한 경우를 위한 우회 방법. 트리거 없이 이미 200을 반환하는 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에서 테스트했습니다.