Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 974
LeonardChallis Avatar asked Jun 12 '14 11:06

LeonardChallis


People also ask

What is the difference between envelope sender and from header?

The envelope headers are the MAIL FROM and RCPT TO parts of the SMTP conversation. The envelope sender is the MAIL FROM address, and the envelope recipients are the RCPT TO addresses.

How can I change my address in Phpmailer?

$mail->SetFrom('[email protected]', 'First Last'); There is no need to use AddReplyTo() this is something completely different. You only need to set your from address (and name optionally) by using SetFrom() .

What is SMTP MailFrom?

MailFrom. This is the sender email address that is used in SMTP (the envelope). It is used by email servers to use as a return path if the message turns out to be undeliverable. When SMTP was first defined in 1982 (the standard that later became RFC5321), it was simply called the 'from' address.

Why do we need PHP mail function?

The mail() function allows you to send emails directly from a script.


1 Answers

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.

like image 157
Jasper N. Brouwer Avatar answered Sep 23 '22 21:09

Jasper N. Brouwer