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:

Issue fork drupal-3055296

Command icon 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

imclean created an issue. See original summary.

imclean’s picture

Thanks @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.

mpp’s picture

Note 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.

imclean’s picture

This is the flow of the Return-Path header in Drupal's default implementation.

Drupal\Core\Mail\MailManager:

    // To prevent email from looking like spam, the addresses in the Sender and
    // Return-Path headers should have a domain authorized to use the
    // originating SMTP server.
    $headers['Sender'] = $headers['Return-Path'] = $site_mail;

This then gets handed off to Drupal\Core\Mail\Plugin\Mail\PhpMail:

    // If 'Return-Path' isn't already set in php.ini, we pass it separately
    // as an additional parameter instead of in the header.
    if (isset($message['headers']['Return-Path'])) {
      $return_path_set = strpos(ini_get('sendmail_path'), ' -f');
      if (!$return_path_set) {
        $message['Return-Path'] = $message['headers']['Return-Path'];
        unset($message['headers']['Return-Path']);
      }
    }
$additional_headers = isset($message['Return-Path']) && ($site_mail === $message['Return-Path'] || static::_isShellSafe($message['Return-Path'])) ? '-f' . $message['Return-Path'] : '';

If $message['headers']['Return-Path'] is set and $return_path_set evaluates 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.ini then $message['headers']['Return-Path'] is not unset and $additional_headers is set to ''. This is a problem.

The first problem is setting the Return-Path header at all.

The second problem is the envelope sender stored in php.ini could be different to the Return-Path header. This means the header could be different to the envelope sender.

Ideally, Drupal would have no reference to Return-Path anywhere, except perhaps a general overview of how the envelope sender works. All variables which influence this could be called something different, such as Envelope-Sender.

imclean’s picture

Version: 8.7.x-dev » 8.8.x-dev
Status: Active » Needs review
StatusFileSize
new3.75 KB

For 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.

imclean’s picture

Something like this would be needed to maintain current behaviour in MailManager->doMail():

$params['envelope_sender'] = $site_mail;
imclean’s picture

We're testing this in a contrib project. PHPMailer SMTP explicitly removes the Return-Path header. It also allows configuring the behaviour of the envelope sender (SMTP command MAIL FROM:).

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

imclean’s picture

Issue summary: View changes
imclean’s picture

Issue summary: View changes
imclean’s picture

There's no need to introduce a new parameter. From header is already seperate to the from address.

imclean’s picture

Status: Needs review » Needs work
imclean’s picture

Version: 8.9.x-dev » 9.1.x-dev
Status: Needs work » Needs review
StatusFileSize
new3.69 KB

Addresses #12.

The next step would be to explicitly remove the Return-Path header. If someone wants to add it they can use a contrib module.

imclean’s picture

StatusFileSize
new3.66 KB

$message['from'] is always set.

jungle’s picture

+++ b/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php
@@ -114,9 +105,9 @@ public function mail(array $message) {
       // On Windows, PHP will use the value of sendmail_from for the
-      // Return-Path header.
+      // envelope sender.

Wrapped too early. Could be

       // On Windows, PHP will use the value of sendmail_from for the envelope
       // sender.

Tagging "Needs subsystem maintainer review".

cilefen’s picture

That may take a while. The Mail subsystem has no assigned maintainer.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

imclean’s picture

Postmastery has a good explanation: About the Return-Path header.

imclean’s picture

imclean’s picture

Category: Task » Bug report

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

imclean’s picture

Title: The problem with setting the "Return-Path" header » Setting the "Return-Path" header doesn't follow RFC 5321
Issue summary: View changes
andypost’s picture

Assigned: Unassigned » berdir
imclean’s picture

Issue summary: View changes

Updated the IS to add some ramifications of having 2 Return-Path headers.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

sanduhrs’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs subsystem maintainer review
StatusFileSize
new4.69 KB

Patch 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.

sanduhrs’s picture

Assigned: berdir » Unassigned
imclean’s picture

Thanks @sanduhrs for keeping this going and including a test.

+++ b/core/tests/Drupal/FunctionalTests/MailCaptureTest.php
@@ -54,6 +54,12 @@ public function testMailSend() {
+    $this->assertArrayNotHasKey('Return-Path', $email['headers']);

The header is case-insensitive so it might not be set to "Return-Path". Would this affect the test at all?

sanduhrs’s picture

StatusFileSize
new5.2 KB

The previous test did not really work as I expected - fixed that.
New patch attached.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record

@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.

imclean’s picture

@alexpott, thanks for the feedback.

@imclean what is the bug here?

Setting the Return-path header 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.

This change will probably cause issues for Drupal mailers - and certainly test fails for some contrib modules.

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.

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.

This is a good point. Which modules are setting the Return-Path header, 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-path header. 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.

imclean’s picture

@Berdir #3165762-11: Add symfony/mailer into core:

One thing to check would be the Return-Path header. I saw that I ended up with two return-path e-mails.

This is what's happening now.

imclean’s picture

Status: Needs work » Needs review

@alexpott,

@imclean what is the bug here?

There is another problem. In my comment in #5 I tried to explain it.

The second problem is the envelope sender stored in php.ini could be different to the Return-Path header. This means the header could be different to the envelope sender.

This happens when both the sending module sets $message['headers']['Return-Path'] and there is a different php.ini value in sendmail_path. It can result in 2 different return-path headers.

This change will probably cause issues for Drupal mailers - and certainly test fails for some contrib modules.

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.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new163 bytes

The 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.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

joelpittet made their first commit to this issue’s fork.

joelpittet’s picture

Status: Needs work » Reviewed & tested by the community

I 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-path works and conflated with From (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 the site_mail.

Moved this to a reroll MR, for the bot

joelpittet’s picture

Issue tags: -Needs change record

I 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.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Tests have failed due to the changes.

joelpittet’s picture

Thanks @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

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.