Send authenticated SMTP email over TLS from WordPress

How to configure TLS for SMTP email in WordPress. I was suprised WordPress is not able to send email using an SMTP server out-of-the-box. Not to mention using authenticated SMTP or TLS transport for security. A quick Google search showed me multiple plugins to handle this, but I wanted to create something myself. Here is how to override the wp-mail() function and send email using authenticated SMTP and StartTLS from WordPress.

Authenticated SMTP and TLS in WordPress – secure email

I haven’t checked how other plugins work, but I was sure that I wouldn’t want my SMTP credentials to be stored in the MySQL database. My thought was that storing the SMTP credentials in the wp-config.php file might be better.

I decided to try something, and it turns out to be pretty easy! Just follow the next few steps and you’ll send emails from WordPress using authenticated SMTP (SMTP AUTH) over a StartTLS/TLS secured connection.

The mail sending function, called wp-mail, is defined in the file pluggable.php. This file is located in the directory /wp-includes. This means we can overrule it with our own function and plugin. To start, just copy that function into a new file. Now locate $phpmailer->IsMail(); and change that to $phpmailer->IsSMTP();.

Later on you have to place your PHPMailer configuration directly below this line. If you’d like more information on some PHPMailer configuration settings, see https://github.com/PHPMailer/PHPMailer/blob/master/README.md, and/or the PHP code found in Part 2 below.

As shown below, you can define your SMTP configuration values in your wp-config.php file. If placed in the plugin file, there is always that possibility of a registered user being able to view the contents of that file. We don’t want that. Now, let’s go.

WordPress SMTP configuration in wp-config.php

We don’t want to store our SMTP credentials in either the MySQL database (wp_options table) or wp-mail.php plugin file. Therefor we need to define it in wp-config.php using PHP‘s mail() function.

In /wp-config.php, simply add:

define( 'SMTP_USER', 'user@example.com' ); define( 'SMTP_PASS', 'Your p4ssword' ); define( 'SMTP_PORT', '25' ); define( 'SMTP_SECURE', 'tls' ); define( 'SMTP_AUTH', true ); define( 'SMTP_HOST', 'smtp.example.com' ); define( 'SMTP_FROM', 'website@example.com' ); define( 'SMTP_FROM_NAME', 'e.g Website Name' ); define( 'SMTP_DEBUG', 0 ); 

WordPress wp-mail.php plugin file for SMTP

The second part is this wp-mail.php file.

File: /wp-content/plugins/wp-mail/wp-mail.php
Copy and paste the wp_mail() function from /wp-includes/pluggable.php, or copy and paste the code below and put it in a new file.

Save that new file as wp-mail.php.

Now create a new folder called wp-mail in your /wp-content/plugins directory, for instance using FTP, and upload your newly created wp-mail.php file to that location.

<?php



if (!function_exists('wp_mail')) {
  add_filter('plugin_row_meta', 'plugin_row_meta', 10, 2);
  function plugin_row_meta($links, $file) {
    if ( !preg_match('/wp-saotn-mail.php$/', $file ) ) {
      return $links;
    }
        
    $links[] = sprintf(
      '<a href="https://www.paypal.me/jreilink">%s</a>',
      __( 'Donate' )
    );
    return $links;
  }

  function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {


    
    extract( apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) ) );

    if ( !is_array($attachments) )
      $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );

    global $phpmailer;


    if ( !is_object( $phpmailer ) || !is_a( $phpmailer, 'PHPMailer' ) ) {
      require_once ABSPATH . WPINC . '/class-phpmailer.php';
      require_once ABSPATH . WPINC . '/class-smtp.php';
      $phpmailer = new PHPMailer( true );
    }


    if ( empty( $headers ) ) {
      $headers = array();
    } else {
      if ( !is_array( $headers ) ) {


        $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
      } else {
        $tempheaders = $headers;
      }
      $headers = array();
      $cc = array();
      $bcc = array();


      if ( !empty( $tempheaders ) ) {

        foreach ( (array) $tempheaders as $header ) {
          if ( strpos($header, ':') === false ) {
            if ( false !== stripos( $header, 'boundary=' ) ) {
              $parts = preg_split('/boundary=/i', trim( $header ) );
              $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
            }
            continue;
          }

          list( $name, $content ) = explode( ':', trim( $header ), 2 );


          $name    = trim( $name    );
          $content = trim( $content );

          switch ( strtolower( $name ) ) {

            case 'from':
              if ( strpos($content, '<' ) !== false ) {

                $from_name = substr( $content, 0, strpos( $content, '<' ) - 1 );
                $from_name = str_replace( '"', '', $from_name );
                $from_name = trim( $from_name );

                $from_email = substr( $content, strpos( $content, '<' ) + 1 );
                $from_email = str_replace( '>', '', $from_email );
                $from_email = trim( $from_email );
              } else {
                $from_email = trim( $content );
              }
              break;
            case 'content-type':
              if ( strpos( $content, ';' ) !== false ) {
                list( $type, $charset ) = explode( ';', $content );
                $content_type = trim( $type );
                if ( false !== stripos( $charset, 'charset=' ) ) {
                  $charset = trim( str_replace( array( 'charset=', '"' ), '', $charset ) );
                } elseif ( false !== stripos( $charset, 'boundary=' ) ) {
                  $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset ) );
                  $charset = '';
                }
              } else {
                $content_type = trim( $content );
              }
              break;
            case 'cc':
              $cc = array_merge( (array) $cc, explode( ',', $content ) );
              break;
            case 'bcc':
              $bcc = array_merge( (array) $bcc, explode( ',', $content ) );
              break;
            default:

              $headers[trim( $name )] = trim( $content );
              break;
          }
        }
      }
    }


    $phpmailer->ClearAllRecipients();
    $phpmailer->ClearAttachments();
    $phpmailer->ClearCustomHeaders();
    $phpmailer->ClearReplyTos();



    if ( !isset( $from_name ) )
      $from_name = 'WordPress';

    

    if ( !isset( $from_email ) ) {

      $sitename = strtolower( $_SERVER['SERVER_NAME'] );
      if ( substr( $sitename, 0, 4 ) == 'www.' ) {
        $sitename = substr( $sitename, 4 );
      }

      $from_email = 'wordpress@' . $sitename;
    }

    
    $phpmailer->From = apply_filters( 'wp_mail_from', $from_email );

    
    $phpmailer->FromName = apply_filters( 'wp_mail_from_name', $from_name );


    if ( !is_array( $to ) )
      $to = explode( ',', $to );

    foreach ( (array) $to as $recipient ) {
      try {

        $recipient_name = '';
        if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
          if ( count( $matches ) == 3 ) {
            $recipient_name = $matches[1];
            $recipient = $matches[2];
          }
        }
        $phpmailer->AddAddress( $recipient, $recipient_name);
      } catch ( phpmailerException $e ) {
        continue;
      }
    }


    $phpmailer->Subject = $subject;
    $phpmailer->Body    = $message;


    if ( !empty( $cc ) ) {
      foreach ( (array) $cc as $recipient ) {
        try {

          $recipient_name = '';
          if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
            if ( count( $matches ) == 3 ) {
              $recipient_name = $matches[1];
              $recipient = $matches[2];
            }
          }
          $phpmailer->AddCc( $recipient, $recipient_name );
        } catch ( phpmailerException $e ) {
          continue;
        }
      }
    }

    if ( !empty( $bcc ) ) {
      foreach ( (array) $bcc as $recipient) {
        try {

          $recipient_name = '';
          if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
            if ( count( $matches ) == 3 ) {
              $recipient_name = $matches[1];
              $recipient = $matches[2];
            }
          }
          $phpmailer->AddBcc( $recipient, $recipient_name );
        } catch ( phpmailerException $e ) {
          continue;
        }
      }
    }



    $phpmailer->IsSMTP();
    $phpmailer->SMTPSecure = SMTP_SECURE;    
    $phpmailer->SMTPAuth   = SMTP_AUTH;      
    $phpmailer->Port       = SMTP_PORT;      
    $phpmailer->Host       = SMTP_HOST;      
    $phpmailer->Username   = SMTP_USER;      
    $phpmailer->Password   = SMTP_PASS;      
    $phpmailer->From       = SMTP_FROM;      
    $phpmailer->FromName   = SMTP_FROM_NAME; 
    $phpmailer->SMTPDebug = SMTP_DEBUG;



    if ( !isset( $content_type ) )
      $content_type = 'text/plain';

    
    $content_type = apply_filters( 'wp_mail_content_type', $content_type );

    $phpmailer->ContentType = $content_type;


    if ( 'text/html' == $content_type )
      $phpmailer->IsHTML( true );


    if ( !isset( $charset ) )
      $charset = get_bloginfo( 'charset' );



    
    $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );


    if ( !empty( $headers ) ) {
      foreach( (array) $headers as $name => $content ) {
        $phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
      }

      if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) )
        $phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
    }

    if ( !empty( $attachments ) ) {
      foreach ( $attachments as $attachment ) {
        try {
          $phpmailer->AddAttachment($attachment);
        } catch ( phpmailerException $e ) {
          continue;
        }
      }
    }

    
    do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );


    try {
      return $phpmailer->Send();
    } catch ( phpmailerException $e ) {
      return false;
    }
  }
}
?>

Try for yourself now: activate the plugin, launch a new and different browser and register yourself as a new user on your blog. If you look at the headers of the confirmation email you received, you’ll see the relevant SMTP and (Start)TLS lines.

For example:

[...]
Received: from www.saotn.org (unknown [IPv6:2a00:f60::2:153])
    (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
    (No client certificate requested)
    (Authenticated sender: user@example.com)
    by some_host.example.org (Postfix) with ESMTPSA id E9E74F80034
    for <some_user@example.org>; Tue, 29 Jul 2014 16:05:30 +0200 (CEST)
Date: Tue, 29 Jul 2014 14:05:30 +0000
To: some_user@example.org
From: Sysadmins of the North <website@saotn.org>
Reply-To: Jan Reilink <jan@saotn.nl>
Subject: [Sysadmins of the North] Your username and password
Message-ID: <a155ef9bd228bd89330da98f1e0391bd@www.saotn.org>
X-Priority: 3
X-Mailer: PHPMailer 5.2.7 (https://github.com/PHPMailer/PHPMailer/)

Send email in WordPress using Google Gmail SMTP servers

Relay WordPress email through Gmail SMTP

Want to send email in WordPress using Google Gmail SMTP servers? Here’s how to send email in WordPress using Gmail. Use the following SMTP configuration in your wp-config.php file. You have to use SSL as SMTP_SECURE option.

define( 'SMTP_USER', 'user@gmail.com' ); define( 'SMTP_PASS', 'p4ssword' ); define( 'SMTP_PORT', '465' ); define( 'SMTP_SECURE', 'ssl' ); define( 'SMTP_AUTH', true ); define( 'SMTP_HOST', 'smtp.gmail.com' ); define( 'SMTP_DEBUG', 0 ); 

Now WordPress sends email through smtp.gmail.com as a relay.

WordPress enhancement proposal

I decided to send my WordPress enhancement to the WordPress developers trough WordPress Trac. Maybe it’s a fine addition to the WordPress core. You can find my enhancement proposal here: Proposal: wp-pluggable.php patch to send email through SMTP, not mail().

wp-includes/pluggable.php patch

--- wp-includes\pluggable.orig.php Wed Jul 30 11:52:55 2014 +++ wp-includes\pluggable.php Wed Jul 30 11:48:25 2014 @@ -433,7 +433,24 @@ } - $phpmailer->IsMail(); + if ( ! USE_SMTP ) { + $phpmailer->IsMail(); + } + else { + $phpmailer->IsSMTP(); + $phpmailer->SMTPSecure = SMTP_SECURE; + $phpmailer->SMTPAuth = SMTP_AUTH; + $phpmailer->Port = SMTP_PORT; + $phpmailer->Host = SMTP_HOST; + $phpmailer->Username = SMTP_USER; + $phpmailer->Password = SMTP_PASS; + $phpmailer->From = SMTP_FROM; + $phpmailer->FromName = SMTP_FROM_NAME; + if ( SMTP_ADD_REPLYTO_EMAIL !== '' && SMTP_ADD_REPLYTO_NAME !== '' ) { + $phpmailer->AddReplyTo(SMTP_ADD_REPLYTO_EMAIL, SMTP_ADD_REPLYTO_NAME); + } + $phpmailer->SMTPDebug = SMTP_DEBUG; + } 

wp-config.php patch

--- wp-config.orig.php Wed Jul 30 12:05:57 2014 +++ wp-config.php Wed Jul 30 12:08:18 2014 @@ -58,9 +58,17 @@ -if(!defined('WP_WINCACHE_KEY_SALT')) { - define('WP_WINCACHE_KEY_SALT', 'wp_2389x#s_'); -} +define('SMTP_USER', 'user@example.com'); +define('SMTP_PASS', 'p4ssword'); +define('SMTP_PORT', '25'); +define('SMTP_SECURE', 'tls'); +define('SMTP_AUTH', true); +define('SMTP_HOST', 'smtp.example.com'); +define('SMTP_FROM', 'website@example.com'); +define('SMTP_FROM_NAME', 'e.g Website Name'); +define('SMTP_ADD_REPLYTO_NAME', 'FirstName LastName'); +define('SMTP_ADD_REPLYTO_EMAIL', 'userName@example.org'); +define('SMTP_DEBUG', 0); (function() { var dropdown = document.getElementById( "archives-dropdown-5" ); function onSelectChange() { if ( dropdown.options[ dropdown.selectedIndex ].value !== '' ) { document.location.href = this.options[ this.selectedIndex ].value; } } dropdown.onchange = onSelectChange; })();   

Loading time: 105 queries, 0.208 seconds using 13836312 bytes memory. Peak memory usage: 14217624 bytes.