Problem/Motivation
Drupal's mail command sets the Return-Path header directly. This is in violation of the relevant RFCs.
https://tools.ietf.org/html/rfc2821#section-4.4
A message-originating SMTP system SHOULD NOT send a message that
already contains a Return-path header. SMTP servers performing a
relay function MUST NOT inspect the message data, and especially not
to the extent needed to determine if Return-path headers are present.
SMTP servers making final delivery MAY remove Return-path headers
before adding their own.
More recent RFCs:
The Return-Path header is set by the SMTP server to the value of the envelope sender (MAIL FROM: SMTP command). When using sendmail or other local MTA this can often be set using the "-f" option.
When Drupal sets the Return-Path header the recipient mail server may reject the email or ignore the header. If the email gets through, it results in 2 headers which can be different and which the recipient might flag as spam.
External mail services, such as Mailgun or Sendgrid, set their own envelope sender (therefore Return-Path header) to capture any bounces. Drupal should not try to influence the header at all.
External SMTP services may use Variable Envelope Return Path.
Proposed resolution
Do not set the "Return-Path" header within Drupal and provide more detailed documentation on Drupal's mail system.
That is, remove Return-Path altogether and set the envelope sender to $message['from']. See https://api.drupal.org/api/drupal/core!core.api.php/function/hook_mail_a...
PHPMailer has resolved the issue:
| Comment | File | Size | Author |
|---|
Issue fork drupal-3055296
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
cilefen commentedComment #3
imclean commentedThanks @cilefen. These days it's probably best to use an external mail delivery service such as Sendgrid or Mailgun which have generous free tiers for smaller websites.
But Drupal does need to cater for all possibilities.
Comment #4
mpp commentedNote that mimemail is setting the return-path as well.
The current code contains a bug as another module might set the return-path (with angle brackets) but then core compares the site-mail to the mail address in angle brackets.
Comment #5
imclean commentedThis is the flow of the
Return-Pathheader in Drupal's default implementation.Drupal\Core\Mail\MailManager:This then gets handed off to
Drupal\Core\Mail\Plugin\Mail\PhpMail:If
$message['headers']['Return-Path']is set and$return_path_setevaluates to FALSE then then the "Return-Path" header value gets assigned to a separate parameter $message['Return-Path'] and the header "Return-Path" is removed. This is perfectly valid, just confusing as it shouldn't be called "Return-Path".If the "Return-Path" is set both as a header and in
php.inithen$message['headers']['Return-Path']is not unset and$additional_headersis set to''. This is a problem.The first problem is setting the
Return-Pathheader at all.The second problem is the envelope sender stored in
php.inicould be different to theReturn-Pathheader. This means the header could be different to the envelope sender.Ideally, Drupal would have no reference to
Return-Pathanywhere, except perhaps a general overview of how the envelope sender works. All variables which influence this could be called something different, such asEnvelope-Sender.Comment #6
imclean commentedFor example, removing "Return-Path" and adding support for an optional parameter "envelope_sender". The comments could include how this can influence the "Return-Path" header.
Comment #7
imclean commentedSomething like this would be needed to maintain current behaviour in
MailManager->doMail():Comment #8
imclean commentedWe're testing this in a contrib project. PHPMailer SMTP explicitly removes the
Return-Pathheader. It also allows configuring the behaviour of the envelope sender (SMTP commandMAIL FROM:).Comment #10
imclean commentedComment #11
imclean commentedComment #12
imclean commentedThere's no need to introduce a new parameter. From header is already seperate to the from address.
Comment #13
imclean commentedComment #14
imclean commentedAddresses #12.
The next step would be to explicitly remove the
Return-Pathheader. If someone wants to add it they can use a contrib module.Comment #15
imclean commented$message['from']is always set.Comment #16
jungleWrapped too early. Could be
Tagging "Needs subsystem maintainer review".
Comment #17
cilefen commentedThat may take a while. The Mail subsystem has no assigned maintainer.
Comment #19
imclean commentedPostmastery has a good explanation: About the Return-Path header.
Comment #20
imclean commentedComment #21
imclean commentedComment #23
imclean commentedComment #24
andypostComment #25
imclean commentedUpdated the IS to add some ramifications of having 2
Return-Pathheaders.Comment #27
sanduhrsPatch is nice and clean and works as advertised.
Rerolling and adding a test.
Addressing issue in #16.
As there's no subsystem maintainer listed for mail setting this to RTBC to attract some attention.
Comment #28
sanduhrsComment #29
imclean commentedThanks @sanduhrs for keeping this going and including a test.
The header is case-insensitive so it might not be set to "Return-Path". Would this affect the test at all?
Comment #30
sanduhrsThe previous test did not really work as I expected - fixed that.
New patch attached.
Comment #32
alexpott@imclean what is the bug here? This change will probably cause issues for Drupal mailers - and certainly test fails for some contrib modules. Yes adhering to RFCs is a good idea but the mail RFCs are a bit notorious. If the header is being ignored by Sendgrid and Mailchimp then isn't the correct thing being done anyway.
At the very least we need a change record. But it would also be great if some analysis of the impact / necessity of this change from the POV of the contrib eco-system.
Comment #33
imclean commented@alexpott, thanks for the feedback.
Setting the
Return-pathheader may have been a design choice for Drupal, probably to get around problems with cheap hosting and non-compliant mail servers, but it isn't the correct behaviour.True there is a "MAY" in the RFC, so mail servers can just ignore the extra header. I think there is confusion in the Drupal community about what the header is used for, how it should be formatted and where it should be set. Originally it was probably being used to help deliverability but I don't think it actually does.
Contrib modules which rely on the header being set by Drupal may need to be updated. There shouldn't be many though, it's it's only related to sending email, not constructing or adding attachments. See: https://www.iana.org/assignments/message-headers/message-headers.xhtml
That said, the proposed change here should only affect those sending email with Drupal core. Other modules which send email are free to do what they like.
This is a good point. Which modules are setting the
Return-Pathheader, or relying on it being set, and why? What does it achieve?This could be a good opportunity to reduce confusion about what should be set where. Modules which construct emails, such as newsletter or MIME modules, might be adding it when they shouldn't be. It's possibly a legacy from earlier versions of the modules which the current maintainers aren't confident in removing. There's understandably a fair bit of caution and inertia when old modules have thousands of users.
As mentioned in #8, we're using the module PHPMailer SMTP, which explicitly unsets the
Return-pathheader. We're using it with Mailgun, Sendgrid, Outlook/Office365 and soon Mailchimp. I'm sure it's been used with other mail services.The Symfony Mailer module also doesn't set the Return-Path header.
Comment #34
imclean commented@Berdir #3165762-11: Add symfony/mailer into core:
This is what's happening now.
Comment #35
imclean commented@alexpott,
There is another problem. In my comment in #5 I tried to explain it.
This happens when both the sending module sets
$message['headers']['Return-Path']and there is a different php.ini value insendmail_path. It can result in 2 different return-path headers.This is Core behaviour so I'm not sure how a contrib module would test it. A module which generates and sends email would check its own input and output, but by this stage the message has been handed off to PhpMail to send the email.
To approach this another way, why is Drupal setting the Return-Path header, against the recommendation of the RFCs?
Set to Needs review to hopefully encourage further discussion.
Comment #37
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #40
joelpittetI agree with @iamclean's assessments and answers to @alexpott from #32.
I love this patch because it removes complexity and clears up misconceptions of how this
Return-pathworks and conflated withFrom(in my case for bounces) and still does the same thing it used to.I'm using mimemail which makes the -f flag not work because the Return-path is in the
<from@example.org>format and never matches thesite_mail.Moved this to a reroll MR, for the bot
Comment #42
joelpittetI did a really rough draft CR https://www.drupal.org/node/3418522, please edit at a will. I'm not sure how to describe it as "envelope sender" but that is the correct term it sounds strange.
Comment #43
alexpottTests have failed due to the changes.
Comment #44
joelpittetThanks @alexpott, usually the test failures would kick it back to needs work, probably a transition to gitlab thing...
Anyways, this patch makes this problem with RFC 2822 Return-Path more problematic
#3257799: RfcComplianceException: RFC 2822 Return-Path
Because prior to this, we stripped the return-path out, so it didn't validate it through Symfony. With this patch it's still there, so gets validate and makes that issue a problem for my case as well... (I haven't applied the patch in that one because I'd rather not rely on multiple patches to solve a problem if I can help it).
I still like the solution here, just need to find a way to make everybody happy... and the testbot