Skip to content

Respect smtp_envelope_to when sending MIME messages#27

Open
marikrebega wants to merge 1 commit intoreadysteady:mainfrom
marikrebega:smtp-envelope-to-support
Open

Respect smtp_envelope_to when sending MIME messages#27
marikrebega wants to merge 1 commit intoreadysteady:mainfrom
marikrebega:smtp-envelope-to-support

Conversation

@marikrebega
Copy link
Copy Markdown

Summary

send_mime now uses mail.smtp_envelope_to as the to parameter
when posting to the Mailgun API.

Behavior

  • When smtp_envelope_to is not explicitly set, it defaults to all
    recipients (to + cc + bcc) — existing behavior is preserved.
  • When smtp_envelope_to is explicitly set, only those addresses
    are used as the SMTP envelope recipients, regardless of the To,
    Cc, and Bcc headers in the MIME message body.

Use case

This allows callers to control exactly which addresses receive the
email at the transport level, independently of the visible headers —
useful for mailing lists, redirects, or BCC-only delivery scenarios.

@marikrebega
Copy link
Copy Markdown
Author

@timcraft, can you please take a look?

@timcraft
Copy link
Copy Markdown
Member

What are you using to verify that Mailgun will deliver to all of the recipients listed in the headers, not just those specified in the to parameter?

@marikrebega
Copy link
Copy Markdown
Author

marikrebega commented Apr 14, 2026

What are you using to verify that Mailgun will deliver to all of the recipients listed in the headers, not just those specified in the to parameter?

@timcraft
The change is backward compatible because smtp_envelope_to defaults to destinations (To + Cc + Bcc) when not explicitly set. It only differs when a caller explicitly overrides smtp_envelope_to.
We've been running this exact change as a monkey patch in production (see attached initializer).

module MailgunnerSmtpEnvelopePatch
  def send_mime(mail)
    to = ["to", Array(mail.smtp_envelope_to).join(",")]

    message = ["message", mail.encoded, { filename: "message.mime" }]

    multipart_post("/v3/#{escape @domain}/messages.mime", [to, message])
  end
end

current_version = Gem.loaded_specs["mailgunner"].version
target_version = Gem::Version.new("3.4.0")

if current_version > target_version
  Rails.logger.warn(
    "[mailgunner] Gem version #{current_version} is newer than #{target_version}. " \
    "Please review MailgunnerSmtpEnvelopePatch in #{__FILE__} to ensure compatibility."
  )
end

Mailgunner::Client.prepend(MailgunnerSmtpEnvelopePatch)

In addition, we also verified this with a non-production Mailgun test send and checked the Mailgun activity logs.

We sent a MIME message where the visible headers contained the full recipient list:

To: recipient-a@example.test, recipient-b@example.test, recipient-c@example.test

Mailgun then emitted separate accepted / delivered events per envelope recipient. In each event:

  • message.headers.to still contains the full header recipient list
  • envelope.targets contains the actual SMTP envelope recipient for that event
  • recipient matches that same envelope target

That shows Mailgun is not delivering only to a single to param value — it is processing each envelope recipient individually while preserving the original MIME headers.

Sanitized Mailgun log excerpts:

{
  "event": "accepted",
  "envelope": {
    "sender": "support-dev@example.test",
    "targets": "recipient-a@example.test",
    "transport": "smtp"
  },
  "message": {
    "headers": {
      "to": "recipient-a@example.test, recipient-b@example.test, recipient-c@example.test",
      "subject": "test split delivery"
    }
  },
  "recipient": "recipient-a@example.test"
}
{
  "event": "accepted",
  "envelope": {
    "sender": "support-dev@example.test",
    "targets": "recipient-b@example.test",
    "transport": "smtp"
  },
  "message": {
    "headers": {
      "to": "recipient-a@example.test, recipient-b@example.test, recipient-c@example.test",
      "subject": "test split delivery"
    }
  },
  "recipient": "recipient-b@example.test"
}
{
  "event": "accepted",
  "envelope": {
    "sender": "support-dev@example.test",
    "targets": "recipient-c@example.test",
    "transport": "smtp"
  },
  "message": {
    "headers": {
      "to": "recipient-a@example.test, recipient-b@example.test, recipient-c@example.test",
      "subject": "test split delivery"
    }
  },
  "recipient": "recipient-c@example.test"
}

@timcraft
Copy link
Copy Markdown
Member

Yes I can see what smtp_envelope_to does, it's the behaviour of Mailgun I was looking for clarity on (it's not documented in the API documentation). Thanks. I'll work on releasing a new version soon.

@marikrebega
Copy link
Copy Markdown
Author

thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants