Why Your WordPress Emails End Up in Spam — And How to Fix It
· 11 min read
A client running a WooCommerce store messaged me: "Customers keep saying they never got their order confirmation. Some found it in spam, but most didn't get anything at all." The store was processing around 80 orders a day. Payments were going through, stock was decrementing, orders were appearing in the WooCommerce dashboard — but customers were left wondering if their order actually went through.
I checked the obvious culprit first. WP-Cron was running fine. Action Scheduler had no stuck jobs. WooCommerce email settings were configured correctly. The emails were being triggered on time. This wasn't a cron problem.
The emails were being sent. They just weren't arriving.
Testing What Recipients Actually See
I placed a test order and watched what happened. The server mail log confirmed the email left the server:
tail -f /var/log/mail.log
Apr 10 14:23:17 server postfix/smtp[12847]: 4F2B61A0023: to=, status=sent (250 2.0.0 OK)
Status: sent. But when I checked my Gmail, the order confirmation was sitting in Spam. Gmail's spam verdict at the top of the email read: "Why is this message in Spam? It doesn't have authentication information and we can't verify that it's really from your domain."
I then forwarded that same email to mail-tester.com — a free tool that scores your email deliverability out of 10. The result: 2.1 out of 10. The report was brutal:
- No SPF record found for the sending domain
- No DKIM signature
- No DMARC record
- Envelope sender doesn't match the "From" header
- Sending IP has a poor reputation score
Every major email provider — Gmail, Outlook, Yahoo — was either sending these emails to spam or silently dropping them. The store had been losing customer communications for months without knowing it.
Why WordPress Emails Fail by Default
WordPress uses a function called wp_mail() to send all emails — order confirmations, password resets, contact form submissions, everything. Under the hood, wp_mail() uses PHP's mail() function, which hands the email to whatever mail transfer agent is installed on the server (usually Postfix or Sendmail).
The problem is that PHP mail() sends email without any authentication. No login credentials, no encryption, no digital signature. The email leaves your server like an unsigned letter with no return address — and modern email providers treat it accordingly.
Here is what happens when your WooCommerce store sends an order confirmation via PHP mail():
- WordPress tells PHP to send an email from "Your Store <[email protected]>"
- PHP hands it to the server's local mail agent
- The mail agent sends it from the server's IP address — which might be shared with 200 other websites on the same hosting account
- The email's envelope sender (the technical "from") becomes something like
[email protected]— completely different from the "From" header your customer sees - The receiving mail server checks: Is this IP authorised to send email for yourstore.com? No SPF record found. Is the email cryptographically signed? No DKIM found. What should I do with unauthenticated email? No DMARC policy found.
- The email goes to spam. Or gets silently dropped.
This isn't a WordPress bug. It is how PHP mail() works on virtually every hosting setup. WordPress just happens to use it as the default.
What Changed in 2024 — And Why This Matters More Now
In February 2024, Google and Yahoo rolled out stricter email authentication requirements. Previously, unauthenticated emails might still land in the inbox — you'd get lucky depending on the server's IP reputation. Now:
- SPF and DKIM are mandatory. Emails without valid SPF and DKIM authentication are far more likely to be rejected outright, not just sent to spam.
- DMARC is required for bulk senders (over 5,000 emails per day), but Google explicitly recommends it for all senders.
- Spam complaint rates must stay below 0.3%. If customers mark your emails as spam because they don't recognise them (because they landed in spam and look suspicious), your domain reputation tanks further.
For a WooCommerce store sending 80 order confirmations a day plus shipping notifications, password resets, and review reminders, this adds up. Microsoft followed with similar requirements for Outlook.com and Hotmail in 2025. The direction is clear: unauthenticated email is on borrowed time.
How to Check Your Current Setup
Before fixing anything, diagnose where you stand. Run these checks from any machine with a terminal.
Check your SPF record:
dig TXT yourstore.com +short | grep spf
If you get no output, you have no SPF record. If you see something like v=spf1 include:_spf.google.com ~all, that covers Gmail but not your WordPress server or transactional email service.
Check your DKIM record:
dig TXT default._domainkey.yourstore.com +short
The selector (the default part) varies by email service. If you are not sure what yours is, check the DKIM-Signature header in any email sent from your domain.
Check your DMARC record:
dig TXT _dmarc.yourstore.com +short
If this returns nothing, you have no DMARC policy. Receiving servers have zero guidance on how to handle unauthenticated email from your domain.
Send a test email from WordPress:
Add this temporary snippet to your theme's functions.php or use a code snippets plugin to test whether wp_mail() is actually working and to capture any errors:
add_action('wp_mail_failed', function($wp_error) {
error_log('wp_mail failed: ' . $wp_error->get_error_message());
});
Then trigger a test email from WooCommerce > Settings > Emails — click any email type and hit "Send test". Check your PHP error log for failures.
The Fix: SMTP and a Transactional Email Service
The solution is to stop using PHP mail() entirely and send through an authenticated SMTP connection to a dedicated email service. This gives you:
- A reputable sending IP (not shared with random websites)
- Automatic DKIM signing on every email
- Proper SPF alignment
- Delivery logs and bounce tracking
- Dramatically better inbox placement
Step 1: Choose a transactional email service
For WooCommerce stores, I typically recommend one of these:
- Brevo (formerly Sendinblue) — Free tier of 300 emails/day. Good enough for stores doing under 300 daily transactional emails. Simple setup. I use this for smaller client stores.
- Postmark — Excellent deliverability, purpose-built for transactional email. Starts at $15/month for 10,000 emails. My go-to for stores where email reliability is revenue-critical.
- Amazon SES — Cheapest at scale ($0.10 per 1,000 emails). More complex setup. Best when you or your developer are comfortable with AWS.
- Mailgun — Developer-friendly, good API. Starts at $0.80 per 1,000 emails after a free tier.
For this client, I went with Brevo — the store was sending around 150 transactional emails a day (order confirmations, shipping notifications, password resets), comfortably within the free tier.
Step 2: Install an SMTP plugin
I use FluentSMTP — it is free, open source, and has no upsells. It supports all the major services and stores email logs so you can verify delivery.
Install it from Plugins > Add New, search "FluentSMTP", install and activate.
Other solid options:
- WP Mail SMTP — the most popular (3 million+ installs), but the free version is limited and pushes you toward paid tiers
- Post SMTP — good alternative with built-in email logging
Step 3: Configure DNS records
Your transactional email service will give you DNS records to add. The exact records vary by provider, but you will typically need to add:
SPF record — a TXT record on your root domain that authorises the service to send on your behalf:
v=spf1 include:spf.brevo.com include:_spf.google.com ~all
This example authorises both Brevo and Google Workspace. Adjust the include: entries for your specific services. One critical rule: you can only have one SPF record per domain. If you already have one, add the new include: to the existing record rather than creating a second one. Multiple SPF records will cause both to fail.
DKIM record — a TXT record that publishes the public key your email service uses to sign outgoing emails. The provider will give you this. It looks something like:
Host: mail._domainkey.yourstore.com
Type: TXT
Value: v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNA...
DMARC record — a TXT record telling receiving servers what to do with emails that fail SPF/DKIM checks:
Host: _dmarc.yourstore.com
Type: TXT
Value: v=DMARC1; p=quarantine; rua=mailto:[email protected]; pct=100
Start with p=quarantine (send failing emails to spam) rather than p=reject (block them entirely). Once you are confident everything is working, you can tighten it to p=reject.
Step 4: Configure the plugin and verify
In FluentSMTP settings, select your provider (Brevo, Postmark, etc.), enter the API key or SMTP credentials, and set the "From Email" to match your domain — for example, [email protected].
The "From Email" must match the domain you authenticated with DNS records. If your DNS records are for yourstore.com, don't send from @gmail.com or @yourothercompany.com. This mismatch is one of the most common causes of deliverability failures I see.
Send a test email from the plugin's settings page. Then verify:
dig TXT yourstore.com +short | grep spf
dig TXT mail._domainkey.yourstore.com +short
dig TXT _dmarc.yourstore.com +short
All three should return your configured records. DNS propagation can take up to 48 hours, but usually completes within an hour.
Step 5: Test your score
Send another test email to mail-tester.com. You should now see a score of 9 or 10 out of 10.
For this client, the score went from 2.1/10 to 9.8/10. The 0.2 deduction was because the sending domain was relatively new and hadn't built up a long reputation yet — that resolves itself over time with consistent, legitimate sending.
Common Mistakes That Undo the Fix
Even after setting up SMTP correctly, I see these issues trip people up:
Multiple SPF records. If your domain has two separate TXT records starting with v=spf1, both will fail validation. Merge them into one. Check with:
dig TXT yourstore.com +short | grep -c "v=spf1"
If this returns 2 or more, you have a problem.
Using a free email address as the "From." Sending WooCommerce emails from [email protected] will fail DMARC checks because Gmail's DMARC policy is p=reject — Gmail tells receiving servers to reject emails claiming to be from @gmail.com that weren't sent through Gmail's own servers. Always use an email address on your own domain.
Forgetting to update WooCommerce "From" settings. WooCommerce has its own email sender settings under WooCommerce > Settings > Emails. Make sure the "From" address here matches what you configured in your SMTP plugin. A mismatch means your WooCommerce emails might bypass the SMTP plugin entirely or fail alignment checks.
Not monitoring after setup. Email deliverability is not a set-and-forget task. IP reputations change, DNS records can be accidentally deleted during domain migrations, and email services occasionally rotate DKIM keys. I check email deliverability quarterly for every site I manage.
A Quick WP-CLI Check for Email Health
If you manage multiple WordPress sites, you can quickly test wp_mail() from the command line:
wp eval 'var_dump(wp_mail("[email protected]", "Test from CLI", "This is a test email from WP-CLI."));'
If this returns bool(true), the email was accepted for delivery (though that doesn't guarantee inbox placement — it just means the sending succeeded). If it returns bool(false), something is broken at the sending level.
For sites using FluentSMTP, you can also check the email log in the WordPress admin under FluentSMTP > Email Logs. Every email sent through the plugin is logged with its delivery status — a massive improvement over the default WordPress setup where emails disappear into the void with no record of success or failure.
The Result
After configuring Brevo via FluentSMTP and adding the proper DNS records, the client's email deliverability transformed. Order confirmations started arriving in customers' primary inboxes within seconds. The "I never got my order email" support tickets dropped to near zero within a week.
This is one of the first things I configure on every new WooCommerce site I onboard. It takes about 20 minutes, costs nothing on the free tier, and prevents a problem that silently damages customer trust and generates unnecessary support work.
If your WordPress site's emails are landing in spam — or you are not sure whether they are — this is worth checking. And if you'd rather not deal with DNS records and SMTP configuration yourself, this is included in my WooCommerce maintenance plans. I also cover this as part of new site onboarding for every maintenance client.
