WordPress Mail Sending: Configuration and Debug

It is always a painful process for those WordPress administrators who want to send their mail.

WordPress is famous for being easy to install, and sure it is. In WordPress, there are several important usage of mail, such as password reset. However, the official instruction on preparation and installation seems to lack mention about the mail. And there is no way to configure the mail during the installation, in wp-config.php or in Settings page. This is unlike most mordern self-hosted server software. I suspect this might be a legacy of an era when most WordPress users hosted their site on a virtual host, and the mail settings are what hosting service providers consider.

As the old adage said that “no two snowflakes are alike”, the install environment of each WordPress instance might be totally different. In regards to the mail sending problem, the solution varies from case to case. There might be many users who are using virtual hosting service. There might also be many users who are running Windows. To be clear, this ariticle is not a complete guide which could cover all the cases. It is mainly written for those who have experience on server administration and run WordPress on their own Unix-like server(either VPS or bare metal server), and have full access to the server(SSH).

When consulting search engine with keywords “WordPress mail”, you may easily find that plenty of articles recomend users to install a plugins to solve the problem. Indeed, many plugins could solve the problem perfectly, especially for users who have no idea how and where the WordPress running. And I would like to recommend using plugins. But if you are willing to go down the rabbit hole, the question now is why WordPress itself isn’t working without plugins. To answer the question, it is necessary to get our hands dirty and look into the source code.

How does mail in WordPress work

It is easy to find that the mail sending in WordPress is handled by the function wp_mail() in wp-includes/pluggable.php. The official page on the function is good in general. And it mentions that the function is “similar to PHP’s mail function”. After looking at the implementation, it uses PHPMailer as the mail sender library. The wp_mail() first parses the arguments and initializes the instance of PHPMailer class. It could also be noticed that the default From address is set to WordPress <wordpress@yourdomain.name>. Then, WordPress hands out the sending work to PHPMailer.

According to the PHPMailer’s documents, the PHPMailer itself supports multiple ways to send the mail, including SMTP. But in wp_mail() function, the following code set PHPMailer to use PHP’s mail() function:

// Set to use PHP's mail().
$phpmailer->isMail();

Then it will execute PHPMailer::send(). The PHPMailer::postSend() will use different sending method according to the PHPMailer::$Mailer. For the cases here, PHPMailer::mailSend() will execute and call PHPMailer::mailPassthru() and finally call mail(). The call stack could be shown as below:

wp_mail() -> PHPMailer::send() -> PHPMailer::mailSend() -> PHPMailer::mailPassthru() -> mail()

Thus, the finally mail() function is the key point. PHP’s manual provides the description on mail and reference on mail(). As the requirements mentions, “For the Mail functions to be available, PHP must have access to the sendmail binary on your system during compile time.”

According to the source code, PHP’s mail() function(with name php_mail() and written in C) will check if sendmail_path is set in php.ini. Then it will call popen(3) to open a pipe to sendmail command at sendmail_path and use this pipe to pass the mail content to the sendmail. If the sendmail_path is not set, the function will return immediately.

Finally, we have reached the bottom of the rabbit hole—sendmail. In short, the WordPress chooses to delegate the mail sending to PHP, and then PHP delegates it to the system.

Before we turning to the topic about sendmail, the question we put forward before—why WordPress itself isn’t working without plugins—could be answer now. Under some environments, the sendmail won’t work or even might not exist, and thus the mail can’t send out. The plugins could use wp_mail filter to rewrite the wp_mail() function, then they could also provide a UI to let the user configure SMTP settings or mail sending API. At last, mail will succeed sending via plugins.

Sendmail

The sendmail command could date back to “the first BSD version that included TCP/IP protocols”. But it seems not being included in the POSIX standard. The original BSD sendmail was used widely in the 1990s. And it might be a de facto standard interface to MTA(Mail Transfer Agent). So there are different implements of sendmail interface in order to be compatible. Except the original sendmail, the popular MTA Postfix provides its sendmail compatibility interface, and the OpenBSD’s default MTA OpenSMTPD also have a sendmail interface. As the default MTA on Debian, Exim4 also provides sendmail interface when execute with name sendmail.

Unfortunately, it could be found that those implements are not so compatible and there might be arguments not work on all the sendmail command line interface. Also, different MTA has a different configuration. So it is important for the admins to know which sendmail is used in the environment that PHP and WordPress run, and investigate its man page or documents to set them up properly.

Thus, for WordPress users who want to send a mail, a working MTA should be installed on the server, in order to provide a sendmail interface. If the only need is to just send a mail via external STMP server, most MTA could be configured to work locally and to send via a so-called “Smarthost”. The configuration instructions for many MTA could be found with keyword “[MTA Name] smarthost” or “[MTA Name] external smtp”. Here we could provide some links for references.

Configure Postfix to Send Email Using External SMTP Servers

Configure SendMail to Use SMTP Relay

Communicating with a smarthost – Exim – Debian Wiki

A light-weight solution

The configuration of MTA might be too complex, if you just want to send mail through external mail service in WordPress. Luckily, there are some light-weight softwares like msmtp to solve the problem. msmtp is an SMTP client with sendmail mode. The following brief instruction will base on Debian Buster. The following are basicly based on the msmtp page on the ArchLinux Wiki.

First, install msmtp-mta from buster-backports repository, since the version in the buster repository is old and the configuration file format has changed.

# apt install -t buster-backports msmtp-mta

Then create and edit the file /etc/msmtprc, replace it with your configuration. Here we show an example configuration only for reference. Please be sure the correct information from your Mail Service Provider is used here. And read the msmtp manual carefully and prevent from exposing the MTA to the public.

account default

host [YOUR MAIL SERVICE SMTP SERVER]
port [YOUR MAIL SERVICE SMTP PORT]
tls on
tls_starttls off
tls_trust_file /etc/ssl/certs/ca-certificates.crt

user [YOUR MAIL SERVICE USERNAME]
password [YOUR MAIL SERVICE PASSWORD]
auth plain

from [YOUR MAIL SERVICE ADDRESS]
set_from_header auto
syslog LOG_MAIL

Start the MTA via systemd.

# systemctl start msmtpd
# systemctl enable msmtpd

Finally, check if the sendmail command which provided by msmtp works, if the php mail() function works, and then WordPress. Note that WordPress’s default From Address might fails the anti-spam policy of some mail service providers. The From Address could be changed by adding wp_mail_from filter function in WordPress Theme’s functions.php.

Debug

It is annoyed when WordPress fails to send out mails. Especially there are several parts involved in the whole process. It is important to know the reason. There are several ways to debug.

First, lookup the mail log file /var/log/maillog. This file could have logged the problems generated by sendmail command. And try to execute sendmail command directly and make output verbose to see if any problem happens.

Second, to see if PHP mail() function works, write a small PHP script like

<?php
mail("youremail@example.com", "Test email from PHP", "msmtp as sendmail for PHP");
?>

and run under both cli and php-fpm.

Third, there is a useful hook provided by WordPress, wp_mail_failed could be used to see what WordPress get if mail sending failed.

Conclusion

WordPress mail sending is a hard problem though, it could be solved with patience. The easiest way is definitely to use plugins. But it might also be worthy to understand what happens under the hood.

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create your website with WordPress.com
Get started
%d bloggers like this: