How can I force the From: header (envelope sender) in PHP without putting it in mail()?
Asked Answered
S

2

9

I have a development web server (CentOS LAMP stack) that uses SMTP relays setup in postfix to send email. We use mailgun with multiple users, a setup similar to this, but with specific users instead of just wildcard emails:

/etc/postfix/main.cf

smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_auth_enable = yes
sender_dependent_relayhost_maps = hash:/etc/postfix/relayhost_map
smtp_sender_dependent_authentication = yes
smtp_sasl_security_options = noanonymous
relayhost = [smtp.mailgun.org]:587

/etc/postfix/sasl_passwd

# our domains
[email protected]  [email protected]:password1
[email protected]     [email protected]:password2
[email protected]  [email protected]:password3

# in-case we don't have it setup, use a default
#[smtp.mailgun.org]:587      [email protected]:password2

/etc/postfix/relayhost_map

[email protected]  [smtp.mailgun.org]:587
[email protected]     [smtp.mailgun.org]:587
[email protected]  [smtp.mailgun.org]:587

To setup email logging , I am authenticating each developer on the machine with their own SMTP credentials. I want to set it up so that developers don't need to add the additional_headers or additional_parameters to get the correct smtp relay match in postfix - and indeed it would take a lot of work to setup different mail headers in code for different developers, especially with versioned code. I digress. This was working fine from postfix's side of things when I use the following:

mail('[email protected]', 'subject', 'message here...', 'From: [email protected]', '[email protected]');

So I then added the following to the vhost configs:

php_admin_value sendmail_path "/usr/sbin/sendmail -t -i [email protected]"

which successfully allowed me to get rid of the -f additional_parameter and still send properly. Then I added the following:

php_value sendmail_from "[email protected]"

In a phpinfo() dump I see the local value for sendmail_from is set correctly, however now when I send the email it comes up as:

[email protected] on behalf of Apache

It seems as if the correct sender (MIME, not envelope, as postfix recognises the authentication and gives 250 Great success). With postfix logging on verbose, I see only references to the correct email from the sender input attribute.

In mailgun, I see the following information from the log, however, for the email when the From: [email protected] isn't used:

...
"envelope": {
    "transport": "smtp", 
    "sender": "[email protected]", 
    "sending-ip": "x.x.x.x", 
    "targets": "[email protected]"
}, 
"message": {
    "headers": {
        "to": "[email protected]", 
        "message-id": "[email protected]", 
        "from": "[email protected] (Apache)", 
        "subject": "Debug Test"
    }, 
    "attachments": [], 
    "recipients": [
        "[email protected]"
    ], 
    "size": 654
},
...

What's interesting is the same log for when From: [email protected] is present, the message->headers->from is set correcetly to [email protected] without the (Apache) addition. Surely this means that it's PHP's fault and that PHP is not utilising the sendmail_from value properly?

So with all this in mind, my resulting question is how can I set the default MIME Sender (From header) in PHP, apart from in the mail() function? Have I missed something with my method/config above, or is it just plain impossible? I'm happy to think outside the box a little, being that this will help save time for the reason we want this feature.

Susansusana answered 12/6, 2014 at 11:19 Comment(0)
L
5

sendmail_from is ignored

From http://www.php.net/manual/en/mail.configuration.php:

sendmail_from string
Which "From:" mail address should be used in mail sent from PHP under Windows. This directive also sets the "Return-Path:" header.

So because you're using a CentOS LAMP stack, not Windows, this setting is ignored.

Also:

sendmail_path string
If set, smtp, smtp_port and sendmail_from are ignored and the specified command is executed.

So sendmail_from is also ignored because you're using sendmail_path.

Solution

You can also pass the -F (capital F) with an empty value to remove the "sender full name" from the header:

Pass -f [email protected] -F "" to the $additional_parameters argument of the mail() function.

Other notes

-f parameter

When you see your sender address changed, Postfix is doing that. This is because (in your case) the system-user under which Apache runs is calling the sendmail binary. And this binary will use <system-user>@<hostname> as sender address (unless told otherwise by the -f or -r parameter).

To prevent this, sendmail should be called with -f <sender address>. Either by passing it to the $additional_parameters argument of the mail() function, or by adding it to the sendmail_path ini-setting.

But it looks like you've already figured this out :)

A better solution

I suggest you don't use the ini-settings at all. In stead, write a small class around the mail() function:

class Mailer
{
    /**
     * @var string
     */
    private $fromEmail;

    /**
     * @var string
     */
    private $fromName;

    /**
     * @param string $fromEmail
     * @param string $fromName
     */
    public function __construct($fromEmail, $fromName)
    {
        $this->fromEmail = $fromEmail;
        $this->fromName  = $fromName;
    }

    /**
     * @param  string $to
     * @param  string $subject
     * @param  string $body
     * @param  array  $headers
     * @return bool
     */
    public function send($to, $subject, $body, array $headers = array())
    {
        $headers[]  = sprintf('From: "%s" <%s>', $this->fromName, $this->fromEmail);
        $parameters = sprintf('-f %s', $this->fromEmail);

        return mail($to, $subject, $body, $headers, $parameters);
    }
}

And use it like so:

$mailer = new Mailer('[email protected]', 'My Company');
$mailer->send('[email protected]', 'Subject', 'Body');

This class is over-simplified, but it should give you a basic idea of how negate the hassle of From: headers and -f parameters every time you want to send an e-mail.

And if you're using a Dependency Injection Container, or a Registry, you can configure/instantiate this class in the bootstrap phase of your application and simply grab it from the container/registry whenever you want to send an e-mail:

$container->get('mailer')->send($to, $subject, $message);

Or using a registry:

Registry::get('mailer')->send($to, $subject, $message);

3rd party library

In to long run it can be worth while to look into 3rd party libraries to send e-mails. I personally prefer Swift Mailer.

Lofton answered 20/6, 2014 at 8:11 Comment(12)
One of the reasons I haven't so far used a 3rd party or written my own mail() wrapper is because of the number of sites and different codebases they all use - this is definitely something long-term I'd like, but not an option yet. We can ignore the -f stuff altogether because as I said the envelope sender is working just fine with my sendmail path config. But the sendmail_from is being ignored altogether, therefore MIME from isn't being set. sendmail_from isn't set in php.ini, only in the local config. Either way it seems to be disregarded. Maybe a bug? I will try other versions of PHP.Susansusana
You are passing the sender address as From: header, so why do you want to set the sendmail_from? (see updated side note).Lofton
I've updated my answer with with a quite definitive reason behind your issue.Lofton
To answer the first question, because I don't want to set the From: header - I want devs to be able to do: mail($to, $subject, $message). Secondly, thanks for explaining why it isn't working. So can you think of another way (via config) around my problem?Susansusana
Unfortunately not (without wrapping mail() in a custom class/function). I also don't see the benefit of telling devs to do mail($to, $subject, $message); in stead of $container->get('mailer')->send($to, $subject, $message); or Registry::get('mailer')->send($to, $subject, $message);Lofton
Sorry, it's a case of the code already being there in many places and not having to go and change them all. I'm thinking maybe a dirty search/replace mail( for Crap::mail( type scenario. What's really frustrating are the amount of sites I've seen saying sendmail_from works on linux too such as sitepoint.com/advanced-email-phpSusansusana
I couldn't leave you hanging like this, so a solution is provided :) -F (capital F)Lofton
It's funny you should mention that - I've been testing ` php_admin_value mail.force_extra_parameters "-f [email protected] -F \"Friendly Name\""` but it still gives me the same problem. I beleive this is the envelope sender name, not the MIME header one?Susansusana
When using a value for -F it will be used as envelope sender fullname. And when a From: header is missing it will be added in the format <address> (<fullname>) (where -f provides the address, -F provides the fullname). When using an empty value for -F the fullname (including brackets) is omitted.Lofton
Is this documented somewhere that I've missed? Because if this is so then there's either conflicting configuration I have or there's a bug with PHP. I am setting -f and -F and not setting anything in mail()Susansusana
Not in such detail, I figured this out by trying ;) But for more info about the parameters you can consult the sendmail man page.Lofton
Well I'm still getting the "on behalf of" showing up, but in the logs it appears to be the correct sender. I'll award the bounty, thanks for your help it's much appreciated. If I find any more interesting information I'll add it here.Susansusana
A
0

Be sure to setup the postfix with the proper smtp sasl map as well. Typically found at /etc/postfix/main.cf

Find, or add:

# SASL
smtp_sasl_password_maps = static:[email protected]:xxxxx 

Where the email is the configured SMTP email, and the xxxxx is the SMTP password you've configured in mailgun.

Give your services (including postfix) a restart and you should be in business.

Amyamyas answered 12/6, 2014 at 15:39 Comment(1)
Thanks, though I have actually done this. See the link at the start of the question based on my setup. I'll add the config to the question when I'm back at a PCSusansusana

© 2022 - 2024 — McMap. All rights reserved.