WordPressTroubleshootingConfigurationHostingWordPress 6.xWordPress 7.xIntermediateCurrent

How to Fix WordPress Emails That Are Not Being Sent

A practical workflow for silent WordPress email failures: capture wp_mail errors, fix the From address, then move to authenticated SMTP or a transactional provider.

Two problems that look like one

“My WordPress site doesn’t send emails” is not one problem. It is at least two, and they need opposite fixes.

The first problem is that the mail is never sent. Something inside your stack — the PHP mail function, a broken SMTP connection, a plugin conflict — throws an error, and the message never leaves the server. The second is that the mail is sent but never arrives, or arrives in spam, because it fails recipient-side authentication checks. If you apply the fix for the second problem to the first, you will waste an afternoon.

Everything WordPress sends — password resets, comment notifications, form confirmations, WooCommerce order emails — goes through a single function: wp_mail(), defined in wp-includes/pluggable.php and documented in the WordPress developer reference. That function hands the message to PHPMailer, and it returns true or false to the calling code. It does not throw a visible error, and most plugins and themes happily ignore the return value. That is why email failures on WordPress are silent by design.

This guide walks through the diagnosis in the order we use on client sites: separate the two failure modes, capture the real error, fix the sender identity, then move to an authenticated sending method.

Step 1: Establish which failure you actually have

Before changing anything, run two cheap tests.

Test A: send to a different address. Trigger a password reset (Users → your account → “Send password reset”) to a Gmail or Outlook address you control. If the mail lands in spam, you have a deliverability problem — skip ahead to Step 4. If nothing arrives anywhere, continue.

Test B: check whether mail works at all on the server. Install a small test through WP-CLI or a temporary mu-plugin that calls wp_mail() and prints the boolean return value:

add_action( 'admin_init', function () {
    if ( ! isset( $_GET['mailtest'] ) ) {
        return;
    }
    $sent = wp_mail(
        'you@example.com',
        'Test from ' . home_url(),
        'Plain text test message.'
    );
    wp_die( $sent ? 'wp_mail returned true' : 'wp_mail returned false' );
} );

Visit /wp-admin/?mailtest=1. If wp_mail() returns false, PHPMailer caught an exception and the message was never handed off. If it returns true but nothing arrives, the message left WordPress and the problem lives downstream — in the server’s mail transfer agent, in DNS, or in the recipient’s spam filters.

Remove this snippet as soon as you are done. It is a diagnostic, not a feature.

Step 2: Capture the real error instead of guessing

When PHPMailer throws an exception, WordPress fires the wp_mail_failed action hook — available since WordPress 4.4 — and passes it a WP_Error object containing the exception message plus the mail data: recipient, subject, message, headers and attachments. The developer reference shows the hook firing in wp-includes/pluggable.php as:

do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_data ) );

Listening to that hook turns a silent failure into a readable one. Create the file wp-content/mu-plugins/mail-error-logger.php (the mu-plugins folder runs automatically and cannot be deactivated from the admin):

<?php
add_action( 'wp_mail_failed', function ( $wp_error ) {
    if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
        error_log( '[wp_mail] ' . $wp_error->get_error_message() );
        error_log( '[wp_mail] ' . print_r( $wp_error->get_error_data(), true ) );
    }
} );

Then enable debug logging in wp-config.php — the same WP_DEBUG and WP_DEBUG_LOG constants we use when hunting down the causes of a WordPress 500 error — and trigger a test send. Read wp-content/debug.log.

Three error messages cover most cases:

  • “Could not instantiate mail function” — PHP’s mail() is unavailable or misconfigured on the server. Many hosts disable it outright because unauthenticated PHP mail is a spam source. Fix: use SMTP or an API-based mailer (Step 4).
  • “SMTP connect() failed” / “Connection refused” — a plugin is already configured for SMTP, but the server cannot reach the SMTP host. Usually a blocked port (25, 465 or 587), a wrong host or a firewall rule. Ask the host which outbound mail ports are open.
  • “SMTP Error: Could not authenticate” — the SMTP credentials are wrong, the password changed, or the provider now requires an app password or API key.

If wp_mail() returned true in Step 1 but nothing arrives, there is no wp_mail_failed entry to read — the failure is downstream. Check the mail log on the server if you have shell access, and check the recipient’s spam folder before anything else.

Step 3: Fix the sender identity

By default, WordPress stamps outgoing mail with the address wordpress@yourdomain.com — no mailbox, no display name, and no DNS records that vouch for it. The wp_mail_from filter, documented since WordPress 2.2, exists precisely to change this, together with its sibling wp_mail_from_name:

add_filter( 'wp_mail_from', function () {
    return 'hello@yourdomain.com';
} );
add_filter( 'wp_mail_from_name', function () {
    return 'Your Site Name';
} );

Two rules matter more than the code. First, use an address on the domain the site sends from. Sending as gmail.com from a yourdomain.com server fails SPF alignment and is a classic spam-folder cause. Second, use an address that actually exists — some hosts refuse to relay mail from non-existent local addresses, and recipients that verify the sender (a bounce check) will penalise you for it.

If you install an SMTP plugin in the next step, set the From address in the plugin settings instead of with filters, so the two stay in sync.

Step 4: Move sending to SMTP or a transactional provider

Once the diagnosis points away from your WordPress configuration itself, the durable fix is to stop relying on the server’s PHP mail and route wp_mail() through an authenticated mailer.

The most established option is the WP Mail SMTP plugin by WPForms, listed in the WordPress.org plugin directory with more than four million active installations. It reconfigures the wp_mail() function to use either plain SMTP credentials or a direct API integration with a transactional provider. The API-based route has a practical advantage worth knowing: the plugin’s documentation notes that emails are sent through the provider’s API, so they go out even if your host blocks SMTP ports — which resolves the “SMTP connect() failed” class of errors without a support ticket to your host. Recent versions also ship WP-CLI commands, so the whole setup can be scripted rather than clicked through, which fits neatly into the update workflow described in our guide to updating WordPress safely with WP-CLI.

Which mailer to pick depends on volume and budget:

  • A mailbox you already own (your domain’s mail host, or a Google Workspace / Microsoft 365 account) is fine for low-volume sites — admin notifications, contact forms, password resets. Watch the provider’s sending limits.
  • A transactional email service is the right choice the moment volumes grow or deliverability matters commercially — WooCommerce order flows, membership emails. These providers maintain the sender reputation, feedback loops and bounce handling that shared hosting cannot.

One configuration rule prevents a whole class of bug: run only one mail plugin. WP Mail SMTP’s own changelog tracks known conflicts with other SMTP plugins; when two plugins both override the PHPMailer configuration, the loser silently wins or breaks. If you are switching from an older SMTP plugin, deactivate it first.

Step 5: Authenticate the domain, not just the plugin

An SMTP plugin fixes transport. Deliverability still depends on DNS records that prove your domain authorised the sending server. Whatever provider you use, complete its DNS setup — at minimum SPF and DKIM, and ideally DMARC. The records are provider-specific and each serious transactional provider documents exactly which records to add and how to verify them; there is no generic record that works for all.

A concrete check that takes two minutes: send a test message through your new setup to a Gmail address, open it, and use Gmail’s “show original” view. The SPF, DKIM and DMARC lines should each say PASS. If DKIM fails, the signing key in DNS does not match the provider’s — regenerate it in the provider dashboard and replace the DNS record.

Step 6: Verify and keep watching

Fixing email once is not the same as knowing it works. Three habits close the loop:

  1. Send a test from the plugin’s email test tool after every configuration change, not just at the end. It catches credential and DNS mistakes while you still remember what you changed.
  2. Log outgoing mail. Email logging (available in WP Mail SMTP Pro, or through dedicated logging plugins in the WordPress.org directory) answers the question that otherwise requires guesswork: did the message leave WordPress, and what did it contain?
  3. Watch bounces at the provider. Transactional providers report bounces, spam complaints and delivery rates per message. A rising bounce rate after a change is your earliest warning.

When the mail stack is not the problem

Two edge cases account for most “I fixed SMTP and it still doesn’t work” reports. First, form plugin settings: many form plugins (and WooCommerce) let you configure recipients and From addresses that override global settings — a notification still going to an old address is a plugin setting, not a mail failure. Second, object caching or a staging copy: if a site was cloned, mail settings may still point at the production SMTP account, which the provider rejects. The wp_mail_failed log from Step 2 distinguishes these in seconds.

And as with any debugging on a production site, the golden rule from our 500-error troubleshooting workflow applies: change one thing at a time, and test after each change. Email failures have enough moving parts — WordPress, the mailer plugin, the provider, DNS, the recipient — that simultaneous changes make the real cause invisible.

If you would rather have this handled end to end, our WordPress development services include mail deliverability setup as a standard part of site builds and rescues.

Related guides

Continue with the surrounding work.

More WordPress guides
Related insights

Broader context from the YCTheme blog.