On this pageHow WordPress sends email out of the box
A WordPress site sends more email than its owner thinks. The contact form notification. The password reset. The new-user welcome, the comment moderation alert, and on a shop every order confirmation, invoice and shipping update WooCommerce has. Nobody designed most of these, and nobody checks them until a customer writes to say the order email never came.
When one of these lands in spam, look at how the message leaves the server before you touch the wording. In WordPress that part is set by default to the least deliverable option there is.
How WordPress sends email out of the box
WordPress itself and nearly every plugin that sends mail, WooCommerce and the form plugins included, go through one function, wp_mail(). It hands the message to PHPMailer, which by default passes it to PHP's mail(), which hands it to whatever mail program the hosting server has. No login to a mail provider, no signing, nothing in your DNS that says this server may send for your domain.
The sender address it picks is wordpress@ followed by your site's domain (WordPress strips a leading www.). If you never changed it, that is what your customers see in the From line, and on many sites nobody has ever created that mailbox.
On shared hosting the message then goes out from the host's server, through an IP address that plenty of other sites share. If one of them sends to a bought list, or gets hacked and starts pumping out spam, the address picks up a bad reputation, and your password resets leave from the same address as theirs.
So a default WordPress install sends mail whose SPF does not line up with your domain. The bounce address usually stays on the host's own server name, and if your address ends up there instead, SPF fails outright. There is no DKIM signature for your domain either, so DMARC fails. Gmail and Yahoo set their sender rules in February 2024, Microsoft did the same for domains sending more than 5,000 messages a day to Outlook.com in May 2025, and since November 2025 Gmail has been rejecting more of the mail that breaks its rules instead of filing it in spam.
Send through a real mail service
The fix that matters most is to stop using mail(). Install an SMTP plugin (WP Mail SMTP, FluentSMTP and Post SMTP are the common ones, and each has a free version) and connect it to a service that sends mail properly: your own mailbox provider, such as Google Workspace or Microsoft 365, or a transactional service like Amazon SES, Brevo, SendGrid or Mailgun.
The plugin replaces the transport under wp_mail(), so every email from every plugin goes the new way without touching the plugins themselves. Use the provider's API option where the plugin has one, rather than plain SMTP with a password. WP Mail SMTP points out that the API route works even when your host blocks SMTP ports.
People skip the next part. Authenticate the domain at that provider: each of them gives you DNS records to publish, usually two or three CNAME records for DKIM and an SPF include or a custom return-path domain. Until those records are in your DNS, the provider puts its own name on the signature, and DMARC fails there just as it did on the host.
The From address on contact forms
Form plugins, Contact Form 7 and WPForms among them, let you choose what goes in the From line of the notification you receive. Many setups put the visitor's address there, so that pressing Reply answers the visitor. It looks clever, and it is exactly what DMARC was built to stop.
Say a visitor called Anna writes from anna@yahoo.com. Your site now sends a message claiming to be from yahoo.com, through a server Yahoo has never authorised. yahoo.com publishes a DMARC policy of p=reject, so a receiver that follows it refuses the notification as forged. With a Gmail visitor the policy is softer and the message more often ends up in spam. WPForms warns about exactly this in its documentation.From is you, Reply-To is the visitor
Set it up this way instead:
- From: an address on your own domain, such as
forms@yourdomain.com, the same domain your SMTP plugin is authenticated for. - Reply-To: the visitor's address, from the form field. Pressing Reply still answers Anna.
- To: the mailbox you actually read.
In Contact Form 7 this is the Mail tab: put your own address in From and add Reply-To: [your-email] under Additional headers. The other form plugins have a Reply-To field in their notification settings.
WooCommerce emails
WooCommerce has its own sender settings: WooCommerce, then Settings, then Emails, in the Email Sender Options section, with a "From" name and a "From" address. Out of the box the address is the site's admin email. If the person who installed the shop used a Gmail address for that, every order confirmation goes out claiming to come from gmail.com, and fails DMARC for the same reason as the contact form above. Put an address on your authenticated domain there; the store name is a good sender name. (For the order notifications sent to you, WooCommerce already puts the customer in Reply-To, which is the right way round.)
Two things go wrong with shop email that do not go wrong with a contact form.
The first is volume. A sale or a newsletter plugin sending to customers through the same route as order confirmations mixes marketing and transactional mail on one reputation. If the newsletter gets complaints, the receipts suffer. Sending the two through different services keeps them apart, and a separate subdomain for marketing helps a little, though receivers still give the main domain's reputation some weight.
The second is the unsubscribe question. Order confirmations and password resets are transactional, and Gmail's and Yahoo's one-click unsubscribe rule does not apply to them. A review request, an abandoned-cart reminder or a "you might also like" email sent to past buyers is marketing, and if you send these at volume, the rule applies to them in full, which means a List-Unsubscribe header with a one-click option. Check whether the add-on that sends them adds that header; the test below shows it either way.
The DNS side, in plain terms
Whatever service you send through, your domain needs three things in DNS for the mail to be trusted. A full walk-through is in SPF, DKIM and DMARC explained; here is what it means for a WordPress site.
- SPF. One TXT record on your domain listing who may send for it. Many sending services need no change here, because they align through their own bounce domain. If yours does ask for an
include:and the domain already has a record for your mailbox provider, add it to that record. Do not create a second one; two SPF records are an error and both stop working. Check the result with the SPF checker. - DKIM. The records your sending service gave you. Once they are live, the service signs every message with your domain, and this is the signature DMARC looks for.
- DMARC. A TXT record at
_dmarc.yourdomain.com. Start withv=DMARC1; p=none; rua=mailto:and an address you read, then tighten it when the reports show nothing of yours failing. The DMARC checker reads what you have.
Keep in mind that the hosting server may still be sending something on its own, a cron job or an old plugin that calls mail() directly instead of wp_mail(). Those messages skip the SMTP plugin, and the DMARC reports will show them as a source you did not expect.
Testing the real emails, not a test message
Every SMTP plugin has a "send test email" button, and all it proves is that the connection works. It does not show you the order confirmation as WooCommerce builds it, the form notification with its Reply-To, or the password reset with the link WordPress puts in it.
So I make WordPress send the real ones. A test address from the home page takes one message, so get a fresh address for each email you want to check. For the form, put the address in as the notification recipient for a minute and submit the form. For WooCommerce, place a test order with the address as the customer email. For the password reset, give a test user the address and request a reset. Each message gets its own report, with SPF, DKIM and DMARC and their alignment, 21 blocklists, content and links, and the bulk-sender rules. The folder at Gmail, Outlook, Yahoo and other providers needs the same message sent to a list of inboxes with a marker in the subject, which a form or a shop order cannot do, so leave that part for emails you send yourself.
If you would rather not make test orders by hand, the free WordPress plugin sends these messages through your site's own wp_mail(), the same path your customers' email takes, and shows the result in the dashboard. It also notices when the site starts sending a kind of email it has not seen before.
When it is still going to spam
If authentication passes, the From address is on your domain and the email still goes to spam, look at the reputation of what you send through. A shared hosting IP on a blocklist is a common reason when you are still using mail(), and the blocklist checker will show it. Content plays a smaller part than people expect. A form notification with a single link and three lines of text rarely trips a filter on its own; a WooCommerce email with a heavy HTML template, one large image and a link shortener can.
Check one message, fix what the report shows, and send it again to a new address.