Best Practices Using the PHP mail Function

From SWCP Support Wiki
Revision as of 18:06, 18 January 2016 by Cheeks (talk | contribs)
Jump to navigationJump to search
Warning This information was originally copied from http://collaborate.extension.org/wiki/Best_Practices_Using_the_PHP_mail_Function

- however that web site seems to be gone, so we are posting it here so it is not lost to the world.

Best Practices Using the PHP mail Function

One of the nice things that you can do with PHP is use the built-in mail() function to provide plaintext (or simple html) email capability within your web application. Here are some simple best practices for using mail() within your own web apps.

A Code Snippet

Here's a code snippet that can be used in your web applications that illustrates some of the best practices:

    $mailFrom = "ValidMailbox@validdomain";
    $mailTo = "ValidToAddress";
    $mailSubject = "Useful Subject:  Here's my subject";
 
    $mailSignature = "\n\n-- \n";
    $mailSignature .= "Your friendly Neighborhood web application.\n";
    $mailSignature .= "For help and other information, see http://yourwebapp/help\n";

    $mailBody ="blahblahblah\n";
    $mailBody .= $mailSignature;

    $mailHeader  = "From: $mailFrom\r\n";
    $mailHeader .= "Reply-To: $mailFrom\r\n";
    $mailHeader .= "X-Mailer: ".MYSITE."\r\n";    
    $mailHeader .= "X-Sender-IP: {$_SERVER['REMOTE_ADDR']}\r\n";
    $mailHeader .= "Bcc: ".MONITORADDRESS."\r\n";	
	
    $mailParams = "-f$mailFrom";
    $mailResult = mail($mailTo,$mailSubject,$mailBody,$mailHeader,$mailParams);

Have a Valid From

Your web applications should make sure to have a valid From: address. In the snippet above, we're setting both the "From:" and the "Reply-To:" email headers to be some valid mailbox @ a valid domain. The "From:" address itself is good enough for the header - but the code snippet uses "Reply-To:" just for good measure.

The first and foremost reason to have a valid From: address for your mail() is that you want the folks you mail to have something to respond back to in case there's a problem. This goes for both applications that you write to send mail to multiple recipients and applications that you write to send mail on behalf of the user of the application to a single recipient. You want to give that person a valid mailbox to respond to - even if it's a shared alias or a "do not respond to this address" mailbox. Some spam filtering software will make sure that the domain of the various return and From: addresses in an email is valid (and not just a valid syntax). Your server administrator may provide for shared mailboxes, or throwaway aliases to facilitate "system generated" emails from your application so that you don't get stuck with getting all that email.

Additionally, with many PHP installations with Apache servers that have a default sendmail or postfix configurations, the way that mail() and PHP communication with the local MTA causes the SMTP "envelope sender" to usually be apache@hostname - where hostname is the actual DNS server name that sits behind any given virtual host. The "envelope sender" is the address listed in the "Return-Path:" header - and controls where the email is sent to in the event that a recipient address bounces.

The implication for both the From: and for having a valid Return-Path is twofold: 1) if the mail bounces, or otherwise has a delivery error, you'll never know about it (the return code from the mail() function is only an indication that PHP handed it off to the local MTA) and 2) it clogs up the mailboxes that are used by your server administrators for directing the postmaster mail for the servers. Using the -f Mail parameter

Because of this "envelope sender" issue, perhaps the most important best practice in this code snippet is the addition of the $mailParams part of the mail() function.

This addition forces PHP to set the "envelope sender" (or the Return-Path: header) to the address you specify. (As a by-product of this, the following header may be added to outgoing messages:

X-Authentication-Warning: hostname: apache set sender to [mailFrom] using -f

(Most users will never see this X-header so it is generally not a problem).

Bcc: A Monitoring Address

As mentioned above, the return code from the mail() function is only an indication that PHP handed it off to the local MTA. It doesn't mean that the mail delivered, and it definitely doesn't mean that the mail system is functioning on your server. One way to test to make sure that the mail system is functioning is to use the Bcc: header to send a copy of the mail to a monitoring address (either your address as the web developer, or some monitored shared mailbox or mail alias). If your web application is sending mail on behalf of a user, you definitely don't want this mail to bounce - so please make sure to use a valid address. In this case our example would have included a PHP constant elsewhere in our application, like:

define("MONITORADDRESS","sharedmail@extension.org");

Again, this only verifies that the mail system is functioning for each email. You may want to send some kind of verification link in your $mailBody that a user must click on to verify email delivery.

Other Good Practices

Good emails usually have some kind of friendly signature. With information about the sender, and URLs to get more assistance and help. Your web application emails should do the same. Another best practice included in this example is that the parameters to the mail() function are all abstracted out into clear variable names. By doing that, and sanity checking the strings assigned to $mailTo, $mailSubject, $mailBody, $mailHeader, and $mailParams - it helps to ensure that the strings passed to the mail() function are valid and won't cause errors in the mail delivery. Additionally, we've added several header fields to help with the identification of the sending script, and how it was called. The X-Mailer: header is a common header used by mail clients to indicate the mail client (or MUA) that sent the mail. In this code snippet, we are using a named constant created at the start of the script using the define() function (we've included a useful bonus snippet for doing that at the end of this page). Another good practice is to get the reported IP address of the web user/browser/client that loaded the page/script that utilized the mail() function. X-Sender-IP: is a common header used for this.

Last but not least

This snippet doesn't demonstrate techniques for this, but you should always make sure your email scripts and programs cannot be discovered and used by spammers and web bots to co-opt your script into sending spam. Verify your program inputs! Some quick tips:

  • Email addresses should be valid and not contain any newlines
  • Subject should not contain any newlines
  • No input should contain any potentially harmful HTML, especially javascript

By following through using these best practices, you can improve upon the use of the mail() in your scripts, making sure that you know where the mail you send is coming from, and the addresses that you send mail to are working and valid!