9

I want to add an additional header to outgoing emails on my sites, so I can easily determine which site sent a given email. (I put a standard functionality plugin on all of my sites, so this is easy enough to do, and configuring my email client to filter and otherwise act on this header would be an amazing time-saver.)

I thought this would be a simple matter of plugging into the wp_mail function, but evidently it isn't.

First, I tried this:

add_filter('wp_mail', 'ws_add_site_header');
function ws_add_site_header() {
    return array('headers' => 'X-WU-Site: ' . parse_url(get_site_url(), PHP_URL_HOST));
}

Here, my array took precedence over anything else that modified the mail settings (say, Gravity Forms), so HTML emails started showing up as bare HTML and not nicely-formatted. Makes sense, since the Content-type: header that GF added was scrubbed. But my header was added to the email. Since others also depend on receiving pretty emails (all my sites' content developers and end-users, among others I'm sure), that isn't acceptable.

One of my colleagues then suggested routing my stuff through wp_parse_args(), thus:

add_filter('wp_mail', 'ws_add_site_header');
function ws_add_site_header($args) {
    $new_header = array('headers' => 'X-WU-Site: ' . parse_url(get_site_url(), PHP_URL_HOST));
    return wp_parse_args($args, $new_header);
}

With that, it's as if my function doesn't exist -- my header isn't added, but neither are others' headers and mail settings stripped.

What's the correct way to add a header to an outgoing email, without scrambling up any other filters that might exist?

  • If you would like simple solution, please use Email Header Footer Free Wordpress Plugin – Prashant Baldha Dec 4 '16 at 3:00
  • That plugin doesn't actually address the problem. It adds a "header" to the body of emails generated by WordPress; I was trying to add an SMTP header, which is a completely different thing. – David E. Smith Oct 26 '17 at 16:51
7

Thanks to the above, I've realized my central mistake -- I didn't quite realize that the arguments being passed in were a multi-dimensional array.

For now, I've re-implemented the function thus:

function ws_add_site_header($email) {
    $email['headers'][] = 'X-WU-Site: ' . parse_url(get_site_url(), PHP_URL_HOST) ;
    return $email;               
}

My reading of the wp_mail() source (see: https://core.trac.wordpress.org/browser/tags/4.4.2/src/wp-includes/pluggable.php#L235) leads me to believe that the headers component can be an array, or a big string, or possibly some horrific mish-mash of the two, but that using an array is probably the safer/more correct option.

I do like the various phpmailer answers, but it just seems a bit cleaner to try to do things using WordPress' built-ins.

6

Here's an alternative using directly the AddCustomHeader method of the PHPMailer instance:

/**
 * Add a custom header.
 * $name value can be overloaded to contain
 * both header name and value (name:value)
 * @access public
 * @param string $name Custom header name
 * @param string $value Header value
 * @return void
 */
public function addCustomHeader($name, $value = null)
{
    if ($value === null) {
        // Value passed in as name:value
        $this->CustomHeader[] = explode(':', $name, 2);
    } else {
        $this->CustomHeader[] = array($name, $value);
    }
}

Here we can see that there are two ways to use it:

Example #1:

Here we only pass the header information in the $name input string, that's seperated with :

add_action( 'phpmailer_init', function( $phpmailer )
{   
    $phpmailer->AddCustomHeader( 
       'X-WU-Site: ' . parse_url( get_site_url(), PHP_URL_HOST ) 
    );  
} );

Example #2:

Here are both $name and $value non-empty:

add_action( 'phpmailer_init', function( $phpmailer )
{   
    $phpmailer->AddCustomHeader( 
       'X-WU-Site', parse_url( get_site_url(), PHP_URL_HOST ) 
    );  
} );
  • Nice to know this answer :) But I am curious to know what effect it will effect if someone set it $args['headers'] = 'key: value' via WP filters ? – Sumit Mar 25 '16 at 17:16
  • The phpmailer_init hook fires later than when the wp_mail filter is applied and the custom headers from that filter are also added through the AddCustomHeader() method, as far as I understand it. @sumit – birgire Mar 25 '16 at 17:29
3

PHP headers are strings. You can not parse them as array. You need to add you additional header as string with \r\n to make sure it is added in next line.

Example:

add_filter('wp_mail', 'ws_add_site_header', 99);
function ws_add_site_header($args) {
    $args['headers'] .= !empty($args['headers']) ? "\r\n" : '';
    $args['headers'] .= 'X-WU-Site: ' . parse_url(get_site_url(), PHP_URL_HOST);
    return $args;
}

Also please note add you additional headers at last with priority 99 so no other plugin can replace it if it is not checking that headers already present. If needed make it 999.

  • 1
    I dug through the source of wp_mail() and it's apparently supposed to be able to accept headers as either arrays, or as big chunks of text, and maybe sometimes the two mixed together. Wouldn't advise doing that generally but it's always an option. – David E. Smith Mar 25 '16 at 21:31
  • @DavidE.Smith is correct. wp_mail accepts an array for the header argument and WP will take care of adding \r\n – Jan Beck Mar 30 '16 at 5:20
  • Please note I've mentioned PHP headers not what wp_mail accept. At last headers are strings. What does wp_mail accept was not the question! @JanBeck – Sumit Mar 30 '16 at 5:32
  • 1
    Yes, that was the question. Why do you bring up PHP headers? – Jan Beck Mar 30 '16 at 6:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.