When WordPress emails stop sending after a migration, the cause is almost always something the move changed: the sending IP, DNS records, SMTP credentials, the PHP mail setup or outbound firewall rules. Check each layer in order (WordPress, transport, server, DNS), compare the old and new server, and verify with a real test message, not the “sent” notice.
This is the checklist I follow from my hosting support runbook when a site’s password resets, contact forms or order emails go quiet after a move. It is ordered so that each step either finds the problem or rules out a whole layer, and it ends with a verification step, because a fix nobody tested is a guess.
Why does a migration break WordPress email?
Email depends on more than the WordPress install you just copied. A migration moves the files and the database, but the things around them often change without anyone noticing:
- The sending IP. If the server sends mail directly, receivers now see a new IP that your SPF record may not authorize and that has no reverse DNS.
- The mail path. The old server may have had a working local mail transfer agent (MTA) such as Postfix behind PHP’s
mail(). The new one may have none, or a differentsendmail_path. - Credentials and secrets. SMTP passwords or API keys stored in
wp-config.phpor environment variables don’t always travel with the database. - Egress rules. Many cloud providers block or restrict outbound port 25 by default, and a hardened server may block other SMTP ports too.
- DNS. Moving the domain to new nameservers can silently drop MX, SPF, DKIM or DMARC records that lived only at the old DNS host.
Any one of these breaks mail while the site itself looks perfectly healthy.
Step 1: What exactly is failing?
Before touching anything, collect the facts. I want answers to five questions:
- Which emails fail: password resets, form notifications, WooCommerce order emails, or all of them?
- What are the sender and recipient domains?
- Are all recipients affected, or only some providers (for example only Gmail, or only the company’s own domain)?
- Was an SMTP plugin or service used before the migration?
- What changed in the move: IP, DNS, PHP version, credentials or mail host?
Then send one controlled test email, to an address you can read, with a subject you can search for later. With WP-CLI you can call wp_mail() directly and print any error WordPress raises:
wp eval 'add_action( "wp_mail_failed", function ( $e ) { echo $e->get_error_message(), PHP_EOL; } ); var_dump( wp_mail( "[email protected]", "Mail test 2026-10-10 A1", "Test body" ) );'
wp_mail() returns true when PHPMailer accepted the message for sending, and false when it failed. The wp_mail_failed action receives a WP_Error with the reason, such as an SMTP authentication error or “Could not instantiate mail function”. A true result only means the hand-off worked, not that the message arrived. One caveat: wp_mail() is a pluggable function, and some mail plugins replace it with their own, which may not fire this action. If the test prints nothing useful, check that plugin’s own log.
Step 2: Is WordPress actually sending?
Start at the top of the stack:
- Confirm
wp_mail()is triggered. A form plugin with a broken notification setting never calls it, and no amount of SMTP debugging will help. - Check the SMTP or email plugin’s log. Most SMTP plugins can log every attempt with the server’s response.
- Check PHP and WordPress errors in the PHP error log and
wp-content/debug.log, if logging is on.
If the WP-CLI test from step 1 works but the form doesn’t, the problem is in the form plugin or its settings, not in mail delivery.
Step 3: Which transport does the site use?
Find out how mail leaves the server. There are three common setups:
| Transport | How it works | What usually breaks after a move |
|---|---|---|
PHP mail() |
PHP hands the message to the binary in sendmail_path |
No MTA installed on the new server, or a wrong path |
| Local MTA | Postfix or Exim on the server delivers to the recipient’s MX | Port 25 blocked, new IP not in SPF, no reverse DNS |
| Authenticated SMTP | A plugin sends through an external provider with a login | Missing credentials, wrong port or encryption, sender not allowed |
For PHP mail(), check what PHP will call. The CLI and PHP-FPM can load different php.ini files, so confirm against the web SAPI too (a temporary phpinfo() page you delete straight after, or your host’s PHP settings screen):
php -i | grep sendmail_path
For authenticated SMTP, verify every setting against the provider’s documentation: host, port, encryption, username, and the sender addresses the account is allowed to use. Many providers reject or rewrite a From address on a domain that isn’t verified with them.
How do you test the SMTP connection from the server?
Test from the new server itself, since that is where the firewall and routing apply. First, can it reach the port at all?
nc -vz smtp.example.com 587
-z only checks that the port accepts a connection, and -v prints the result. If this hangs, add -w 5 for a five-second timeout; a timeout usually means an outbound firewall rule or a provider block, while “connection refused” means nothing is listening on that host and port.
Then check the TLS handshake the way an SMTP client would:
openssl s_client -connect smtp.example.com:587 -starttls smtp
This connects, issues STARTTLS and prints the certificate chain. Look for a successful handshake and a certificate that matches the hostname. Once connected you can type EHLO example.com and the server lists its extensions, including the AUTH methods it accepts. Type QUIT to leave. Port 465 uses TLS from the first byte instead, so test it without -starttls: openssl s_client -connect smtp.example.com:465.
If you block outbound traffic on the server, this is where those rules bite. I wrote about how to block outbound traffic to a specific IP with iptables; the same rules, copied to a new server, can stop SMTP without anyone remembering they exist.
Step 4: What do the server logs say?
Check the mail, PHP and web server logs for authentication failures, blocked ports, timeouts or rejected messages. With a local Postfix on Ubuntu, delivery attempts go to /var/log/mail.log (or the journal, with journalctl -u postfix), and stuck messages show up in the queue:
sudo tail -n 50 /var/log/mail.log
mailq
A rejection from the receiving server usually says why: an SPF failure, a missing reverse DNS entry, a blocklisted IP or a policy rejection. Search for the test subject or the recipient address so you read the right lines. If mail sits in the queue with connection timeouts, outbound port 25 is probably blocked.
Step 5: Are the DNS records still right?
Verify the mail-related records for the sending domain:
- MX: where mail to the domain goes. It matters for replies and for mail the site sends to its own domain.
- SPF: which servers may send for the domain.
- DKIM: the public key receivers use to check the message signature.
- DMARC: the policy receivers apply when SPF or DKIM don’t align with the
Fromdomain. - Reverse DNS (PTR): needed when the server sends mail directly. Large receivers distrust IPs without it.
dig +short MX example.com
dig +short TXT example.com
dig +short TXT selector._domainkey.example.com
dig +short TXT _dmarc.example.com
dig +short -x 203.0.113.10
Replace selector with the DKIM selector your provider gave you, and 203.0.113.10 with the new server’s IP. If the sending IP changed, confirm SPF authorizes the new sender. That only matters when the server sends directly; if the site sends through an SMTP provider, the provider’s servers are the senders, and SPF should include the provider instead. After a nameserver move, query the new authoritative nameservers directly (dig @ns1.example.net example.com TXT) to confirm the records exist there and not only in a resolver’s cache.
Step 6: What changed between the old and new server?
If the layers above don’t reveal the problem, compare the source and destination side by side:
wp-config.php, including any mail-related constants- SMTP plugin settings
- credentials and secrets, including environment variables
- PHP version and extensions
- firewall and egress rules
- sender-domain configuration at the mail provider
The difference that broke mail is usually in this list. If the old server is still running, it’s the best reference you have, which is one reason to keep it until the migration is confirmed stable. Other migration problems, such as garbled characters, have their own checks; see how to check your WordPress database charset and collation.
Step 7: How do you verify the fix?
A WordPress “sent” result does not prove inbox delivery. Verify properly:
- Send a new, uniquely identifiable test with a fresh subject, so you know you’re looking at the new message and not an old one.
- Confirm acceptance in the SMTP or provider logs. The provider’s dashboard or the local mail log should show the message accepted by the recipient’s server.
- Inspect SPF and DKIM results in the received headers. In Gmail, open the message and choose “Show original”. Look for
spf=pass,dkim=passanddmarc=passin theAuthentication-Resultsheader. - Test the real workflow: a password reset, a form submission, an order email. These use their own sender settings and templates, so a working test email doesn’t prove they work.
Raw headers are long and hard to read. An AI assistant is useful for one narrow job here: paste the headers, ask which hop rejected or flagged the message and what each authentication result means. Remove addresses and tokens first, and confirm its reading against the header lines yourself before changing DNS.
Quick reference
# 1. Controlled test from WordPress
wp eval 'add_action( "wp_mail_failed", function ( $e ) { echo $e->get_error_message(), PHP_EOL; } ); var_dump( wp_mail( "[email protected]", "Mail test A1", "Test body" ) );'
# 2. Transport
php -i | grep sendmail_path
nc -vz smtp.example.com 587
openssl s_client -connect smtp.example.com:587 -starttls smtp
# 3. Server logs (local Postfix)
sudo tail -n 50 /var/log/mail.log
mailq
# 4. DNS
dig +short MX example.com
dig +short TXT example.com
dig +short TXT selector._domainkey.example.com
dig +short TXT _dmarc.example.com
dig +short -x 203.0.113.10
If the whole site is misbehaving after the move, not just email, start with my WordPress troubleshooting checklist. And if you use an SMTP plugin, keep it updated and treat its API key like a password; the Gravity SMTP key leak shows why. The wp_mail() reference documents the function and its hooks.
FAQ
Why are WordPress emails not sending after I moved hosts?
Usually because the new server has no working mail path (no local MTA behind PHP mail()), outbound SMTP ports are blocked, the SMTP credentials didn’t move, or the new sending IP isn’t authorized in SPF. Test wp_mail() with WP-CLI and read the error it returns.
Do I need to update SPF after a WordPress migration?
Only if the server sends mail directly and its IP changed. If the site sends through an SMTP provider, SPF should include that provider, and the web server’s IP doesn’t matter for SPF.
WordPress says the email was sent. Why didn’t it arrive?
“Sent” means PHPMailer handed the message off. It can still be rejected by the receiving server or filtered to spam. Check the provider or mail log for acceptance and the received headers for SPF, DKIM and DMARC results.
Should I use PHP mail() or SMTP for WordPress?
Authenticated SMTP through a mail provider is easier to make reliable: it has logs, a stable sending reputation and clear DNS requirements. PHP mail() depends on the server’s own MTA, IP reputation and reverse DNS, which is exactly what a migration changes.