PHP mail() and The Path of No Return
Recently, I learned something new about one of the oldest technologies I regularly use – email. Once you get beyond putting a simple plain text email together for sending from a web app, email can get pretty complicated. Sending things in multiple formats (such as HTML with a plain text fall-back), sending attachments, using different encodings and so on, generally there’s quite a lot to know about mail.
One thing I didn’t really know much about was the SMTP envelope. Whilst the mail has a set of headers indicating the subject, who it’s from and who it’s to, the envelope that surrounds that mail has a bunch of headers too. It was these envelope headers that were causing me an issue on one site I run.
I had a message forwarded from a rather irate user who was complaining that he wasn’t receiving our emails because our server was sending from an invalid email address. He had his mail server configured to reject any email sent from an invalid address (I guess to combat spam) and so was rejecting our mails when they got to him. I was sending using PHP’s mail()
function, and was dutifully setting the From:
header to a valid address, so at first didn’t get what was going on.
Turns out the problem was with the From address on the envelope, and not in the message headers themselves. PHP’s mail()
function uses Sendmail to, well, send mail. On being given a message to send that has no explicit envelope From address set, Sendmail will make up an address of current-user@server-name. If you’re lucky this might coincide with a real email address, but in a lot of cases it won’t. This was the circumstance I was coming up against, and the reason our emails weren’t getting through.
Turns out (via a very helpful comment on the php.net page for mail()) that lesser-spotted fifth argument to mail()
can be used to send an additional parameter to Sendmail to set the envelope From address. "-r from
example.com”@
For good measure, you should also set the Return-Path
header on the mail (typically to the same address), as this is also sometimes checked for validity. I’ve been using PHP’s mail()
function for years without knowing this, so I thought it was probably worth sharing.
I did a bit of spot checking in my mailbox of emails I’d recently received from other online services. This default current-user@server-name envelope address and Return-Path
is incredibly common. I know Shaun uses PHP over at Mint, and his emails looked exactly like mine. (As an aside – I really recommend Mint. I use it on a couple of sites and it’s both inexpensive and full of awesomeness.)
Of course, it’s valid to question whether it’s wise to be rejecting email on the basis of a mistake is some obscure mail headers that perhaps not a lot of people know about. However, by the same reasoning, if I’m going to suggest people should be liberal in what they receive, I need to uphold my end of the bargain and be strict in what I send.