A WooCommerce email that never arrives has usually gone wrong in one of three places: WooCommerce did not try to send it, your server handed it over and the mail server refused it, or it was delivered and filed as spam. WooCommerce 10.9 and later record every attempt to send one of its emails, so you can tell which of the three you have in a few minutes. For the last two, the lasting fix is usually to send through a mail service, with the right credentials, instead of the web server.
Did WooCommerce try to send it?#
Start with the order the customer is asking about, because it already holds most of the answer.
- Open the order under WooCommerce → Orders and read the order notes. Since WooCommerce 10.9, every email attempt adds a private note:
Email "Processing order" sent., orEmail "Processing order" failed to send:followed by the reason. - Look at the order status. The customer's confirmation and the shop's New order email go out when an order moves from Pending payment to Processing, On hold or Completed. An order still at Pending payment has sent no email yet: the payment never completed, and that is a payment problem.
- Go to WooCommerce → Settings → Emails, open the email in question with Manage, and check that Enable this email notification is ticked. For emails to the shop, such as New order, check the Recipient(s) box as well.
- Go to WooCommerce → Status → Logs and open the log source
transactional-emails. Each attempt is one line: sent, failed to send with the reason, not sent because the email type is disabled, or not sent withno_recipient.
To send one again once it is fixed, use the Order actions box on the order: Send order details to customer or Resend new order notification.
Not sent, or sent and filed as spam?#
A log line saying "sent" means WooCommerce handed the email to WordPress's mailer and nothing failed on your server. It does not mean the email reached an inbox.
So ask the customer to look in their spam or junk folder, and send yourself a test (see the test section below) to an address at Gmail and one at Outlook.com. If the test lands in spam, the email is going out and receiving servers do not trust it: sending through a mail service helps, and so do the SPF, DKIM and DMARC records on your domain. If the test never arrives anywhere, or the log says "failed", read on.
Why WordPress email goes missing#
WordPress does not send email itself. wp_mail() hands each message to PHP's own mail function, which passes it to whatever the web host has set up. What happens next depends entirely on the host. On some hosting nothing is set up, and the message goes nowhere. On many others it is a plain mail program on a shared server, sending with no reputation of its own and often from an address such as wordpress@yourdomain, which WordPress uses when nothing else sets a From address.
The awkward part is that wp_mail() reports success as soon as the hand-over works. WordPress's own documentation for the wp_mail_succeeded hook says as much: it only means the message was processed without errors, not that anyone received it. So a site can "send" happily for months while customers receive nothing.
Send a test and read what the server said#
You need one test you can repeat after every change. Two work without any extra plugin.
From WooCommerce: open any email under WooCommerce → Settings → Emails. Below its settings is Email preview, with a Send a test email button. The attempt is logged under transactional-emails as a test.
From the command line, if you have WP-CLI:
wp eval 'var_dump( wp_mail( "you@example.com", "Test from my shop", "Hello" ) );'bool(true) means the hand-over worked; bool(false) means it failed on your server. To see why, log the reason. This snippet writes every failure to wp-content/debug.log when WP_DEBUG_LOG is switched on in wp-config.php:
add_action( 'wp_mail_failed', function ( $error ) {
error_log( 'Mail failed: ' . $error->get_error_message() );
} );The wp_mail_failed hook fires whenever PHPMailer, the library behind wp_mail(), gives up. Common ones, and what each means:
Could not instantiate mail function.The host has no working mail program for PHP. Send through a mail service (below).535 5.7.8, orSMTP Error: Could not authenticate.The username or password was refused. Use the credentials the service asks for, which for most services are not your account login.550or553naming the sender, orThe following From address failed:The From address is not one this service will send as. Verify the address, or its domain, with the service.SMTP Error: Could not connect to SMTP host., or a timeout. Nothing answered on that port. Your host may block it; try another port the service publishes.- A reply starting with
4, such as421or451. A temporary refusal, often a rate limit, so try again later. The SMTP standard treats 4xx replies as temporary and 5xx replies as permanent.
The fix: send through a mail service#
A mail service is a company whose business is getting email accepted: Google Workspace or Gmail, Microsoft 365, SendGrid, Mailgun, Postmark, Brevo, Amazon SES and others. Your site signs in to the service over SMTP, and the service delivers the message under its own reputation, can sign it for your domain once you set that up, and gives a clear answer when it refuses. If you already pay for Google Workspace or Microsoft 365 for your own mailbox, you can send through that. Prices and free allowances change, so compare them on each service's own pricing page.
WordPress has no SMTP setting of its own, so you need either an SMTP plugin or a few lines of code. The code route uses the phpmailer_init hook. Keep the password out of the database by defining it in wp-config.php:
// In wp-config.php
define( 'MYSHOP_SMTP_USER', 'apikey' );
define( 'MYSHOP_SMTP_PASS', 'your-key-here' );
// In a small plugin or your child theme's functions.php
add_action( 'phpmailer_init', function ( $phpmailer ) {
$phpmailer->isSMTP();
$phpmailer->Host = 'smtp.sendgrid.net';
$phpmailer->Port = 587;
$phpmailer->SMTPSecure = 'tls';
$phpmailer->SMTPAuth = true;
$phpmailer->Username = MYSHOP_SMTP_USER;
$phpmailer->Password = MYSHOP_SMTP_PASS;
} );Check the credentials carefully, because for most services neither the username nor the password is the one you sign in with:
- Gmail and Google Workspace want an app password, which Google only offers on accounts with 2-Step Verification switched on.
- SendGrid's username is the literal word
apikeyfor every account, and the password is your API key, as its SMTP set-up page says. - Amazon SES wants SMTP credentials created for SES; your AWS secret access key is a different thing.
- Microsoft 365 accepts your address and password only while your administrator allows that kind of sign-in. Microsoft's announcement says it is switched off by default for existing organisations at the end of December 2026, administrators can switch it back on, and a final removal date will be announced in the second half of 2027.
Then set the From address under WooCommerce → Settings → Emails → Email sender options (the "From" address box) to an address the service lets you send as, usually one on a domain you have verified with it. WordPress's own emails, such as password resets, still go out from wordpress@yourdomain until something changes it; the wp_mail_from filter does.
Keep a record, so "I never got it" has an answer#
WooCommerce's transactional-emails log and the order notes now answer the first question: did the shop try, and did the hand-over work. Two limits are worth knowing. The log names the recipient by username, or guest for a customer without an account, not by address. And it is kept for 30 days unless you change the logging settings. Neither shows what happened after the hand-over; your mail service's own dashboard may.
Without a plugin that retries, a refusal is final: wp_mail() returns false and the message is gone, because neither WordPress nor WooCommerce tries it again. A rate limit or a short outage at the mail service can still drop an order confirmation, so look at the log after any busy day.
If it still fails#
- Your host blocks the mail ports. DigitalOcean, for example, blocks ports 25, 465 and 587 on its Droplets. Some services also accept port 2525 (SendGrid's set-up page lists it), and your host can tell you which outgoing port it allows.
- Two SMTP plugins, or a plugin plus your own snippet, are both configuring the mailer. Whichever runs last wins, so keep one.
- A plugin sends from its own From address. A contact form or membership plugin may set one the mail service will not accept, even though WooCommerce's address is fine.
- The email goes out but the customer typed their address wrong. Check the address on the order, correct it, save, and send again from Order actions.
Doing this with Email Delivery#
Email Delivery is our plugin for this job. Under Email Delivery → Settings → How email leaves you choose a mail service from a list, and the host, port and encryption are filled in, with a line saying what that service's username and password actually are. Sending a test shows the whole conversation with the mail server, with the username and password taken out, and when the port is blocked it tries the other ports the service publishes and offers the one that answered.
The log records every message the site sends: who it went to, the subject, which plugin asked for it, and whether the mail server accepted it. With WooCommerce, each order's screen gets a box listing the emails sent about it, with Send it again for the ones WooCommerce can rebuild (Emails about a WooCommerce order). With Keep a refused message and try again switched on, a refused message is kept and tried again after ten minutes, then twenty, then forty, up to the number of attempts you set. A refusal also puts a notice on every wp-admin screen and sends you an email, at most once a day.
What it does not do: "Sent" in its log means the mail service accepted the message, not that it reached the inbox. It does not keep the message itself unless you switch that on. And it does not sign in to Microsoft 365 the newer way (OAuth), so once Microsoft switches password sign-in off, a Microsoft 365 site needs another service, or a backup connection through one, unless its administrator switches that sign-in back on.
Checklist: check it worked#
- Send a test to a Gmail address and an Outlook.com address you can open. Both arrive, and in the inbox.
- Place a real order with a cheap product, or a coupon that makes it free, and confirm the customer email and the New order email both arrive.
- Read the order notes: each email shows as sent.
- Check WooCommerce → Status → Logs, source
transactional-emails: no "failed" lines since the change. - Repeat the test after any change to your host, DNS, mail service password or SMTP plugin.
The plugin for this
Email Delivery
Send your site's email through a real mail service, and see what happened to every message.
From $19 a year · every feature · 30-day refund
![The Send a test screen of Email Delivery in WordPress admin. A red box reads It did not go. SMTP Error: Could not authenticate. Below, a panel headed What the mail server said, noting it is safe to paste into a support request, shows the full conversation with smtp.sendgrid.net, the sign-in lines replaced by [credentials hidden], ending in 535 Authentication failed: The provided authorization grant is invalid, expired, or revoked.](https://www.traslatability.com/wp-content/themes/traslatability/assets/screens/email-delivery-by-traslatability/crops/01-test-transcript-m.webp?ver=1792726162)
In the documentation
- The problem this solves Documentation
- Setting up a mail service Documentation
- Sending a test Documentation
- The log Documentation
- Emails about a WooCommerce order Documentation
- When a message is refused Documentation
- Why does WordPress email go missing in the first place? Knowledge base
- What is a "mail service" and do I have to pay for one? Knowledge base
- My username and password are right and it still says they are wrong Knowledge base
- A customer says they never got their order email Knowledge base
- A customer says their email never came, but the log says Sent Knowledge base
- The test says "connection timed out" Knowledge base
- The test says the sender is not verified Knowledge base