WordPress sends every notification — password resets, new user registrations, comment alerts, contact form submissions, WooCommerce order confirmations — through one function, wp_mail(). Out of the box that function hands the message to PHP's mail(), which is why so many WordPress sites find their email silently disappearing or landing in spam. This guide routes wp_mail() through Postwing's authenticated SMTP relay instead.
There are two separate failures, and both are fixed by the same change:
mail() shells out to a local sendmail binary. Most managed and cloud hosting ships without a local mail server, so the call succeeds, wp_mail() returns true, and the message is discarded. Sending over an authenticated SMTP relay solves both: the relay signs each message with DKIM for your verified domain, and it sends from IPs your SPF record already covers, so DMARC aligns.
| Setting | Value |
|---|---|
| SMTP host | smtp.postwing.app |
| Port | 587 |
| Encryption | STARTTLS (the connection is upgraded to TLS before login) |
| Username | The login of an SMTP token for your domain |
| Password | The password of that token — shown once, when the token is created |
WP Mail SMTP is the most widely used option and has a built-in connection test. Install it from Plugins → Add New, then open WP Mail SMTP → Settings and fill in:
| Field | Value |
|---|---|
| From Email | noreply@your-domain.com — must be on your verified domain |
| Force From Email | On — stops other plugins overriding the sender |
| From Name | Your site name |
| Mailer | Other SMTP |
| SMTP Host | smtp.postwing.app |
| Encryption | TLS (this is STARTTLS, despite the label) |
| SMTP Port | 587 |
| Auto TLS | On |
| Authentication | On |
| SMTP Username | The login of an SMTP token for your domain |
| SMTP Password | That token's password |
Then open the Email Test tab, send a message to yourself and confirm it arrives.
Entered through the settings screen, the SMTP password is stored in the WordPress database in recoverable form. Define it in wp-config.php instead — WP Mail SMTP reads these constants and greys the corresponding fields out in the UI:
/* Add ABOVE the "That's all, stop editing!" line in wp-config.php */
define( 'WPMS_ON', true ); // let constants override the UI
define( 'WPMS_MAILER', 'smtp' );
define( 'WPMS_SMTP_HOST', 'smtp.postwing.app' );
define( 'WPMS_SMTP_PORT', 587 );
define( 'WPMS_SSL', 'tls' ); // 'tls' = STARTTLS, 'ssl' = port 465
define( 'WPMS_SMTP_AUTH', true );
define( 'WPMS_SMTP_USER', 'token-login@your-domain.com' );
define( 'WPMS_SMTP_PASS', 'your-token-password' );
define( 'WPMS_MAIL_FROM', 'noreply@your-domain.com' );
define( 'WPMS_MAIL_FROM_NAME', 'Your Site' );
define( 'WPMS_MAIL_FROM_FORCE', true ); // override plugins that set their own From FluentSMTP is free with no paid tier and supports fallback connections. Go to Settings → FluentSMTP → Add Another Connection, pick Other SMTP, and enter the same host, port 587, TLS encryption and your token credentials. Tick Store password in wp-config.php file so it is written there rather than to the database.
phpmailer_init. With two active, whichever runs last wins and the behaviour depends on plugin load order — which looks exactly like an intermittent delivery bug. Deactivate the others before adding one. WordPress exposes the underlying PHPMailer instance through the phpmailer_init action, so a small must-use plugin is all you need. This avoids a plugin update ever silently changing your mail setup:
<?php
/**
* Plugin Name: SMTP for wp_mail
* Description: Routes every wp_mail() call through an authenticated SMTP relay.
*
* Save as wp-content/mu-plugins/smtp-mailer.php — mu-plugins load automatically
* and cannot be deactivated by accident from the admin.
*/
add_action( 'phpmailer_init', function ( $phpmailer ) {
$phpmailer->isSMTP();
$phpmailer->Host = 'smtp.postwing.app';
$phpmailer->Port = 587;
$phpmailer->SMTPSecure = 'tls'; // STARTTLS; use 'ssl' with port 465
$phpmailer->SMTPAuth = true;
$phpmailer->Username = SMTP_USER; // defined in wp-config.php
$phpmailer->Password = SMTP_PASS;
$phpmailer->Timeout = 10;
} );
// Send as your own domain, not as wordpress@your-server-hostname
add_filter( 'wp_mail_from', fn() => 'noreply@your-domain.com' );
add_filter( 'wp_mail_from_name', fn() => 'Your Site' ); Define SMTP_USER and SMTP_PASS in wp-config.php alongside it.
Each plugin has its own test button, but WP-CLI exercises the real wp_mail() path, which is what your themes and plugins actually call:
# Send a test message straight through wp_mail()
wp eval "var_dump( wp_mail( 'you@example.com', 'SMTP test', 'It works.' ) );"
# Show the underlying error if it returns false
wp eval "add_action('wp_mail_failed', fn(\$e) => print_r(\$e->get_error_message())); wp_mail('you@example.com','SMTP test','It works.');"If it fails, log the full SMTP conversation temporarily:
// Temporarily log the SMTP conversation to debug a failing connection.
add_action( 'phpmailer_init', function ( $phpmailer ) {
$phpmailer->SMTPDebug = 2;
$phpmailer->Debugoutput = 'error_log';
}, 100 );SMTPDebug writes the entire session — including the authentication exchange — to your PHP error log. Remove it as soon as the connection works. | Symptom | Cause and fix |
|---|---|
SMTP connect() failed | Host blocks outbound mail ports. Change the port to 8587 (TLS) or 8465 (SSL). |
SMTP Error: Could not authenticate | Wrong token login or password, or Authentication is switched off in the plugin. |
| Test email works, contact form does not | The form plugin sets its own From address. Enable Force From Email. |
| Mail arrives but goes to spam | The From address is not on your verified domain — check it is not wordpress@… plus your server hostname. |
| Settings look right but nothing changed | A second SMTP plugin is active, or a caching plugin is serving a stale wp-config.php opcache entry. |
| 550 sender rejected | The From domain is not verified, or the token does not cover it. |
By default wp_mail() hands the message to PHP's mail() function, which passes it to a local sendmail binary. On most modern hosting there is no mail server behind it, so nothing is ever delivered. Even where one exists, the message is unauthenticated and fails SPF, DKIM and DMARC checks at the recipient. Routing wp_mail() through an authenticated SMTP relay fixes both problems.
WP Mail SMTP is the most widely installed and has the clearest setup wizard. FluentSMTP is fully free with no paid tier and adds fallback connections. Post SMTP is a good third option with detailed logging. Any of them work — pick one and only one, because two SMTP plugins active at once will fight over the phpmailer_init hook.
Use 587 with the encryption set to TLS, which means STARTTLS. Choose SSL only with port 465. Mismatching the two — SSL on 587 or TLS on 465 — is the most common cause of the plugin's connection test failing.
Usually because the From address is not on the domain you authenticated. If WordPress sends as wordpress@your-server.hosting-provider.com while your DKIM and SPF records are on your-domain.com, nothing aligns and DMARC fails. Set the From address to something on your verified domain and enable the plugin's Force From Email option.
It is safer than the plugin's settings screen, which stores the password in the database where a database dump or a compromised admin account exposes it. wp-config.php sits outside the database and is not served by the web server. Best of all is to keep the constant's value in an environment variable your host provides.
Yes. WordPress fires the phpmailer_init action before every send, so a small must-use plugin that sets the PHPMailer instance to SMTP mode is enough. It avoids a plugin update ever changing your mail configuration, at the cost of no admin UI, no test button and no logging.
Many shared hosts block outbound connections on ports 25, 465 and 587 to limit spam from compromised sites. Switch the port to 8587 for STARTTLS or 8465 for SSL and the connection will establish.