WordPress “works” right up until you need an email to land. Password resets vanish. WooCommerce receipts get quarantined. Contact forms arrive only when Mercury is retrograde.
If you’re sending mail from WordPress and it’s going to spam (or not arriving at all), you don’t have an “email plugin” problem. You have an identity problem: your domain isn’t proving it’s allowed to send, and modern mailboxes don’t accept vibes as authentication.
What’s really happening when WordPress mail hits spam
Mailbox providers don’t “hate WordPress.” They hate unverifiable senders, inconsistent identity, and spam-like sending patterns. WordPress is often the messenger, not the culprit.
When you click “Send,” at least four identities are involved:
- Envelope sender / Return-Path: where bounces go. Usually controlled by the SMTP server or provider.
- Header From: what users see. Often set by WordPress, a form plugin, or a “From email” setting.
- DKIM d= domain: the domain that cryptographically signs the message (if DKIM is used).
- Sending IP: the server that actually delivered the SMTP transaction.
SPF evaluates the sending IP against the envelope domain. DKIM validates the signature against a public key in DNS. DMARC checks whether either SPF or DKIM passes and aligns with the Header From domain.
If you don’t control these identities deliberately, your mail will look like a forgery even when it’s legitimate. And mail systems are paid to distrust you by default.
Opinionated guidance: stop trying to make PHP mail() behave. Use authenticated SMTP or a transactional email provider. Then make your DNS records match reality. That’s the whole game.
Fast diagnosis playbook (check first/second/third)
First: confirm what identity is being used
- Grab a real message that landed in spam (or a recipient’s “show original”).
- Write down: Header From, Return-Path, DKIM d=, sending IP, and whether SPF/DKIM/DMARC passed.
Bottleneck you’re hunting: misalignment (DMARC fail even when SPF passes) or “no authentication at all.”
Second: validate DNS records are present, singular, and correct
- Check you have exactly one SPF TXT record for the domain.
- Check DKIM TXT exists for the selector(s) actually used.
- Check DMARC record exists and is syntactically valid.
Bottleneck you’re hunting: “permerror” due to multiple SPF records, missing DKIM key, DMARC record typo, or wrong subdomain.
Third: confirm WordPress is sending through the intended route
- If you expect SMTP: verify the WordPress plugin is configured and the server can reach the SMTP host on the right port.
- If you expect local MTA: verify Postfix/Exim is relaying through a reputable smarthost, not sending directly from a random VPS IP.
Bottleneck you’re hunting: you fixed DNS for “Provider A,” but WordPress is still sending via “Server B.” This is the most common self-own.
Interesting facts and a little history (because email is older than your CTO)
- Email predates the web. The first network email systems existed in the early 1970s; authentication came decades later, as an afterthought.
- SPF started as a pragmatic hack. It was built to let domain owners publish which IPs can send, because SMTP itself didn’t care.
- DKIM was born from two competing systems. DomainKeys and Identified Internet Mail were merged into DKIM—an “ops compromise” with cryptography.
- DMARC is basically policy glue. It ties together SPF and DKIM plus alignment rules, then adds reporting so you can see who’s impersonating you.
- Alignment exists because attackers can pass SPF on a different domain. Without alignment, a message can “authenticate” but still lie in the visible From header.
- Mailing lists are DKIM’s natural enemy. Modifying message bodies breaks DKIM signatures; the industry created ARC to reduce collateral damage.
- “p=reject” didn’t become common until major providers pushed it. DMARC existed for years before many organizations were brave enough to enforce it.
- SPF has a DNS lookup limit of 10. That limit is why “just add another include” eventually detonates in production.
- Some providers treat unauthenticated mail as suspicious by default now. The bar moved; what worked in 2016 is a liability in 2025.
SPF, DKIM, DMARC: what they are and what they are not
SPF: “This IP is allowed to send for this domain”
SPF is a DNS TXT record that lists which IPs (or which other domains’ SPF records) are authorized to send mail claiming to be from a domain—specifically, the envelope sender domain (Return-Path). The receiver checks the connecting IP against that policy.
SPF is not: encryption, a guarantee of inbox placement, or a guarantee the visible “From:” header is truthful. SPF can pass while the From header is something else entirely.
What SPF is good at: preventing casual spoofing and helping receivers score your mail as legitimate when combined with DKIM/DMARC.
DKIM: “This message was signed by a domain with this key”
DKIM adds a signature header that covers selected headers and (usually) the body. The receiver fetches a public key from DNS (selector + domain) and verifies the signature.
DKIM is not: a promise the content is good, or that the human-friendly From header is aligned with the signer. DKIM only says “a domain with control of this DNS published key signed this content.”
DKIM failure modes: body modified in transit, wrong selector, wrong key in DNS, or your sending system didn’t sign at all.
DMARC: “Accept or reject based on alignment and authentication; send me reports”
DMARC lives at _dmarc.yourdomain as a TXT record. It tells receivers:
- What to do if mail fails DMARC (none/quarantine/reject).
- Where to send aggregate and forensic reports.
- How strict alignment should be (relaxed vs strict).
DMARC passes if either SPF passes with alignment or DKIM passes with alignment.
DMARC is not: an anti-spam filter. It’s an identity enforcement mechanism. Spam filters still judge your content and reputation.
One dry truth: email authentication is like a seatbelt. It doesn’t prevent accidents, but you’ll notice when it’s missing.
Quote: “Hope is not a strategy.” — Gene Kranz
Joke #1: SPF is basically your domain saying, “Yes, that IP is with me.” It’s like VIP list security, but with more DNS and fewer velvet ropes.
Alignment: the part everyone skips and then suffers
If you remember only one thing: DMARC doesn’t care that SPF or DKIM passed. It cares that they passed for the same domain users see in From.
Relaxed vs strict alignment
- Relaxed (adkim=r, aspf=r): subdomains can align. Example: From is
example.com, DKIM signermail.example.comcan align (organizational domain matches). - Strict (adkim=s, aspf=s): exact match required. From is
example.com, DKIM signer must be exactlyexample.com.
Common alignment failure in WordPress land
You send mail “From: you@yourdomain.com” but your SMTP provider uses a bounce domain like bounces.provider-mail.net. SPF passes for that provider domain, but it doesn’t align with yourdomain.com. DKIM might also sign as the provider’s domain if you didn’t set up custom DKIM.
Result: SPF=pass, DKIM=pass, DMARC=fail. The most confusing “everything passed but it failed” you’ll see.
What to do: configure the provider to sign with your domain (custom DKIM) and/or use a custom return-path (custom bounce domain) that aligns with your From domain.
Practical tasks: commands, outputs, and decisions (12+ real checks)
These are the checks I actually run when email is misbehaving. Each one includes: the command, what the output means, and what decision you make next.
Task 1: Find your public sending IP (if you’re sending direct from a server)
cr0x@server:~$ curl -s ifconfig.me
203.0.113.42
Meaning: if your MTA is sending directly, this is likely the IP receivers see.
Decision: if this IP is a generic VPS range with no reputation, don’t send direct. Use a smarthost/transactional provider.
Task 2: Check reverse DNS (PTR) for the sending IP
cr0x@server:~$ dig +short -x 203.0.113.42
wp01.mail.example.com.
Meaning: receivers like to see a PTR that maps the IP to a hostname that looks intentional.
Decision: if you get nothing or a provider-default PTR, fix PTR with your ISP/cloud or stop sending direct.
Task 3: Verify forward-confirmed reverse DNS (FCrDNS)
cr0x@server:~$ dig +short wp01.mail.example.com A
203.0.113.42
Meaning: PTR points to hostname; hostname resolves back to the same IP.
Decision: if it doesn’t match, fix DNS (A record) or PTR. Some receivers penalize mismatches.
Task 4: Inspect SPF record (and catch the “two SPF records” disaster)
cr0x@server:~$ dig +short TXT example.com
"v=spf1 include:_spf.google.com include:spf.protection.outlook.com -all"
Meaning: you have a single SPF string. Good.
Decision: if you see multiple separate v=spf1 strings returned, you must merge into one SPF record.
Task 5: Validate SPF syntax and the 10-lookup limit risk
cr0x@server:~$ python3 - <<'PY'
import dns.resolver, re
domain="example.com"
txt=[]
for r in dns.resolver.resolve(domain,"TXT"):
s=b"".join(r.strings).decode()
if s.startswith("v=spf1"):
txt.append(s)
print("SPF records:", len(txt))
print(txt[0] if txt else "none")
print("includes:", len(re.findall(r'\\binclude:', txt[0])) if txt else 0)
PY
SPF records: 1
v=spf1 include:_spf.google.com include:spf.protection.outlook.com -all
includes: 2
Meaning: simplistic count, but it flags runaway include chains.
Decision: if you’re near 10 DNS lookups, flatten SPF or remove unused senders before SPF becomes permerror.
Task 6: Check DMARC record exists and is readable
cr0x@server:~$ dig +short TXT _dmarc.example.com
"v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com; adkim=r; aspf=r; pct=100"
Meaning: DMARC is published with policy quarantine, relaxed alignment, and aggregate reports.
Decision: if missing, publish one. If policy is p=none, you’re monitoring only (fine temporarily, not forever).
Task 7: Check DKIM selector record for your provider
cr0x@server:~$ dig +short TXT s1._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..."
Meaning: DKIM public key exists for selector s1.
Decision: if empty/NXDOMAIN, either you published the wrong selector or the provider isn’t signing with your domain yet.
Task 8: Confirm what Postfix thinks its identity is (common on self-hosted WordPress)
cr0x@server:~$ postconf -n | egrep 'myhostname|mydomain|myorigin|relayhost|inet_interfaces'
myhostname = wp01.mail.example.com
mydomain = example.com
myorigin = $mydomain
relayhost = [smtp.mailprovider.net]:587
inet_interfaces = loopback-only
Meaning: server is configured to relay through a smarthost, not send directly; also only listens on loopback (good hardening).
Decision: if relayhost is empty and the server is sending direct, expect deliverability pain unless you manage reputation seriously.
Task 9: Verify SMTP connectivity to your relay (network and firewall reality check)
cr0x@server:~$ nc -vz smtp.mailprovider.net 587
Connection to smtp.mailprovider.net 587 port [tcp/submission] succeeded!
Meaning: the server can reach the SMTP submission port.
Decision: if it fails, fix outbound firewall rules, security groups, or provider restrictions (some clouds block 25/587 by default).
Task 10: Send a test email from the server with explicit envelope sender
cr0x@server:~$ printf "Subject: SMTP test\nFrom: wp@example.com\nTo: you@example.net\n\nhello\n" | sendmail -f bounce@example.com you@example.net
Meaning: this tests local MTA path and sets the envelope sender to bounce@example.com.
Decision: if bounces return or logs show rejection, examine MTA logs and authentication to relayhost.
Task 11: Read mail logs for immediate rejects (the truth is in the logs)
cr0x@server:~$ sudo tail -n 50 /var/log/mail.log
Dec 27 10:42:11 wp01 postfix/smtp[21455]: to=, relay=smtp.mailprovider.net[198.51.100.10]:587, delay=1.2, delays=0.1/0.1/0.6/0.4, dsn=2.0.0, status=sent (250 2.0.0 queued as 9A1BC2D3)
Meaning: Postfix handed mail to the relay successfully (2.0.0 sent).
Decision: if you see 5xx rejects, fix authentication, From domain policies, or provider account status before touching DNS.
Task 12: Check WordPress is actually using SMTP (not falling back to PHP mail)
cr0x@server:~$ grep -R "WP_MAIL_SMTP" -n /var/www/html/wp-config.php
37:define('WP_MAIL_SMTP_HOST', 'smtp.mailprovider.net');
38:define('WP_MAIL_SMTP_PORT', 587);
39:define('WP_MAIL_SMTP_AUTH', true);
Meaning: configuration exists (method varies by plugin). This suggests WordPress intends to use SMTP.
Decision: still verify with plugin logs or by correlating timestamps in mail.log. Config drift is real.
Task 13: Inspect a received message’s authentication results (from headers)
cr0x@server:~$ python3 - <<'PY'
import sys, re
raw = """Authentication-Results: mx.example.net;
spf=pass smtp.mailfrom=bounce.example.com;
dkim=pass header.d=example.com header.s=s1;
dmarc=pass header.from=example.com
"""
print("SPF:", re.search(r"spf=\\w+", raw).group(0))
print("DKIM:", re.search(r"dkim=\\w+", raw).group(0))
print("DMARC:", re.search(r"dmarc=\\w+", raw).group(0))
PY
SPF: spf=pass
DKIM: dkim=pass
DMARC: dmarc=pass
Meaning: this is what “good” looks like: DMARC pass with aligned identities.
Decision: if DMARC fails, check alignment: is header.from different from smtp.mailfrom and header.d?
Task 14: Detect multiple SPF records (explicitly)
cr0x@server:~$ dig +short TXT example.com | grep -c "v=spf1"
2
Meaning: two SPF records exist. That’s an SPF permerror for many receivers.
Decision: merge them into a single SPF record immediately. “But it worked last week” is not a mitigating control.
Task 15: Check that your domain isn’t missing the provider’s include
cr0x@server:~$ dig +short TXT example.com | tr ' ' '\n' | grep -E '^include:'
include:_spf.google.com
include:spf.protection.outlook.com
Meaning: SPF includes two ecosystems. That might be correct, or it might be legacy.
Decision: if you no longer send from one of them, remove it. Unused includes expand blast radius and lookup count.
DNS records that actually work (examples you can adapt)
DNS is where good intentions go to die. Keep it boring and correct.
SPF: one record, minimum necessary senders, explicit policy
Example: you send from a transactional provider plus Microsoft 365. You do not send direct from your web server.
cr0x@server:~$ cat spf-example.txt
example.com. 300 IN TXT "v=spf1 include:spf.protection.outlook.com include:send.mailprovider.net -all"
What it means: only those included systems may send; everything else fails hard (-all).
Decision: use ~all only during transitions. Leaving ~all forever is like leaving your office door unlocked because keys are inconvenient.
DKIM: publish the keys your sender uses
Your provider will tell you the selector and the TXT value. Publish it exactly. Don’t wrap it “nicely.”
cr0x@server:~$ cat dkim-example.txt
s1._domainkey.example.com. 300 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..."
Decision: rotate keys periodically, but don’t rotate them “for fun” during a marketing launch week.
DMARC: start with monitoring, then enforce
DMARC is policy. Treat it like one.
cr0x@server:~$ cat dmarc-monitoring.txt
_dmarc.example.com. 300 IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; adkim=r; aspf=r; fo=1"
Decision: run p=none long enough to learn who is sending on your behalf. Then move to quarantine, then reject.
DMARC enforcement example
cr0x@server:~$ cat dmarc-enforce.txt
_dmarc.example.com. 300 IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@example.com; adkim=s; aspf=r; pct=100"
Meaning: strict DKIM alignment, relaxed SPF alignment. This is a common pattern when you rely heavily on DKIM signing with your exact domain.
Decision: strict alignment is not a flex. Use it only when you’re sure all legitimate senders align exactly.
Subdomain strategy: separate transactional mail from human mail
If your marketing platform, WooCommerce, and password resets are all sharing example.com, you’ve combined reputations. Sometimes that’s fine; sometimes it’s a slow-motion incident.
Consider sending transactional mail from mail.example.com or notify.example.com while keeping human mail on example.com. You still need DMARC and alignment, but you’ve separated blast radius.
WordPress sending models: PHP mail vs SMTP vs API
Option A: PHP mail() (default). Cheap. Fragile.
On many hosts, mail() hands off to a local MTA or shared infrastructure you don’t control. The From header might be rewritten. DKIM might be absent. SPF might point to something else. You get the kind of deliverability you deserve for delegating identity to chance.
Use it only if: your host provides authenticated, signed, aligned sending for your domain (some do; most don’t).
Option B: Authenticated SMTP via a plugin. The default sane approach.
WordPress submits mail to an SMTP server you control or rent. That SMTP server signs with DKIM and manages bounces. You set SPF/DMARC for the domain. Everyone is happier.
Key decisions:
- Use port 587 with STARTTLS (or 465 implicit TLS) depending on provider guidance.
- Use an account dedicated to the site, not someone’s personal mailbox.
- Make sure the provider can sign DKIM as your domain (not theirs).
Option C: Email API (HTTP) to a transactional provider. Very reliable, less “SMTP drama.”
This avoids SMTP network blocks and credential weirdness. The provider still sends email, but your application talks HTTPS.
Operationally: great. Deliverability-wise: still requires SPF/DKIM/DMARC alignment. APIs don’t bypass physics.
Joke #2: Sending email without DKIM in 2025 is like deploying without backups: technically possible, spiritually concerning.
Three corporate mini-stories from the deliverability trenches
Mini-story 1: The incident caused by a wrong assumption
They migrated their WordPress site from a managed host to a shiny new Kubernetes cluster. The application worked. Checkout worked. Password resets worked in staging. The go-live happened on a Thursday afternoon, as tradition demands.
Then support tickets rolled in: nobody was receiving order confirmations. Not “going to spam.” Just missing. The marketing team re-sent campaigns from their platform and those landed fine. That was the first clue: only the WordPress-originated transactional mail was failing.
The wrong assumption was simple: “Our domain has SPF and DMARC already, so email auth is done.” It wasn’t. Their SPF authorized Microsoft 365 and a marketing tool. Their new sending path was a Postfix pod egressing from a NAT IP that wasn’t in SPF. DKIM signing wasn’t configured at all, because the old host had quietly signed outbound mail on their behalf.
Receivers did what they should do: SPF failed, DKIM missing, DMARC failed, policy was quarantine for the domain. Some providers spam-foldered it; others rejected it outright depending on local policy and reputation signals.
The fix was also simple, but not quick: they stopped direct sending and routed all transactional mail through a transactional provider with domain DKIM, then updated SPF to include it. Only after DMARC reports showed clean alignment did they move policy back toward reject. The lesson: “We have DMARC” doesn’t mean “this new sender is authorized.” It means “this new sender will be punished if it isn’t.”
Mini-story 2: The optimization that backfired
A company decided to “reduce DNS lookups” by aggressively flattening SPF and removing includes. On paper, this is a valid goal: SPF’s 10-lookup limit is real, and includes can become a dependency chain you don’t control.
The backfire came from process, not principle. They flattened SPF using a snapshot of the provider’s current IP ranges, then deleted the include mechanism that would have tracked changes automatically. It was faster. It was lean. It was also frozen in time.
Weeks later, their provider added new sending infrastructure and began delivering from IPs not listed in the flattened SPF. Some receivers treated SPF fail as a strong negative, and DMARC started failing for a subset of messages because DKIM signing was intermittently disabled during a provider-side rollout incident. The overall result looked like “random deliverability issues,” which is the worst kind—because it sends teams on superstition hunts.
They ended up reintroducing includes for providers that change frequently and flattening only the parts they truly controlled. They also added a weekly check that samples DMARC aggregate reports for unexpected sources. The lesson: optimizing deliverability controls without an update mechanism turns your “hardening” into a time bomb.
Mini-story 3: The boring but correct practice that saved the day
Another org had a habit that was deeply unsexy: any time a new SaaS tool wanted to send mail “from our domain,” it went through a short review. They checked the From domain, whether the vendor supported custom DKIM, what the return-path domain would be, and whether DMARC alignment would pass.
A product team rolled out a new WordPress form plugin integrated with a helpdesk tool. The vendor’s default behavior was to send as noreply@vendor.example with a reply-to of the customer’s address—fine. But the product team wanted it to look branded: support@theircompany.com. That’s where DMARC kills dreams.
Because the review existed, they caught the issue before production: the vendor couldn’t align DKIM to the company’s domain unless they configured a custom sending domain and published DKIM records. Without that, DMARC would fail under their p=reject policy.
They did the boring thing: created a subdomain support-mail.theircompany.com, published DKIM, added a minimal SPF include, and set a dedicated DMARC policy for that subdomain. Launch day arrived. Emails landed. Nobody noticed. That’s the best outcome operations ever gets.
Common mistakes: symptom → root cause → fix
1) “SPF passes but DMARC fails”
Symptom: Authentication-Results shows spf=pass and dmarc=fail.
Root cause: SPF passed for the envelope domain, but the envelope domain doesn’t align with the visible From domain.
Fix: use a custom return-path/bounce domain aligned to your From domain, or rely on DKIM aligned to your From domain (custom DKIM).
2) “DKIM fails only for some recipients”
Symptom: Some providers show DKIM pass, others fail; or failures started after adding a mailing list/forwarder.
Root cause: intermediary modifies the message body or headers covered by DKIM, breaking the signature.
Fix: sign with relaxed canonicalization (provider setting), avoid systems that rewrite bodies, or rely on SPF+alignment where appropriate. For mailing lists, ARC can help but is not universal.
3) “SPF permerror”
Symptom: Headers show spf=permerror or receivers treat SPF as failing despite correct IP.
Root cause: multiple SPF records published, or SPF exceeds DNS lookup limits.
Fix: merge into one SPF record; flatten or prune includes; remove legacy services.
4) “Everything authenticates but Gmail still spams it”
Symptom: SPF/DKIM/DMARC pass, yet inbox placement is poor.
Root cause: reputation/content problems: sudden volume spikes, low engagement, spammy templates, bad list hygiene, or sending IP reputation.
Fix: segment transactional vs marketing, warm up volumes, reduce complaint rate, and ensure consistent sending patterns. Authentication is the entry ticket, not the whole concert.
5) “WordPress From address is @yourdomain but sending is via shared host”
Symptom: Messages show yourdomain in From, but Return-Path is the host’s domain; DMARC fails or is quarantined.
Root cause: shared host rewrites envelope sender; you don’t have aligned authentication for your domain.
Fix: use SMTP/API with your own sending domain and DKIM; stop relying on shared outbound mail.
6) “After enabling p=reject, some legitimate mail disappears”
Symptom: users stop receiving certain automated emails (CRMs, ticketing, old printers, random SaaS).
Root cause: those senders weren’t aligned; DMARC enforcement did exactly what you asked.
Fix: inventory senders via DMARC reports, then either migrate them to aligned sending (preferred) or change their From domain to a subdomain you control with proper auth.
7) “Contact form replies go to the wrong place or fail”
Symptom: you set From to the user’s email so “reply” goes to them; now DMARC fails.
Root cause: you’re impersonating the submitter’s domain in From; receivers detect it.
Fix: keep From as your domain; put the submitter in Reply-To. This is non-negotiable for DMARC.
8) “Microsoft 365 works, but WooCommerce receipts don’t”
Symptom: human mail is fine; WordPress transactional mail has issues.
Root cause: you authenticated one sender ecosystem but not the other. SPF includes 365, but WordPress sends via server IP or another provider.
Fix: route WordPress mail through the same aligned system (365 SMTP with proper auth, or a transactional provider) and update SPF/DKIM accordingly.
Checklists / step-by-step plan
Step-by-step: get WordPress transactional mail out of spam reliably
- Pick a sending path you can control. Prefer a transactional provider or authenticated SMTP. Avoid direct-to-MX from a random web server.
- Define the From domain strategy. Decide if you send from
example.comor a subdomain likemail.example.com. - Configure WordPress to use SMTP/API. Verify it with logs (server mail logs, provider logs, or message headers).
- Publish DKIM for that sender. Ensure DKIM signs as your domain (not the provider’s domain). Confirm selector in DNS.
- Publish exactly one SPF record. Include only legitimate senders for that domain/subdomain. Keep lookup count sane.
- Publish DMARC at monitoring first (if you’re new). Use
p=none, collect reports, identify unknown senders. - Fix alignment issues uncovered by reports. Move senders onto aligned DKIM and/or aligned return-path domains.
- Enforce DMARC. Move to
p=quarantinethenp=reject. Don’t do “reject” on Friday afternoon. - Separate marketing and transactional streams. Different subdomains if needed; different reputations on purpose.
- Re-test with real recipients and real headers. Don’t trust plugin “test succeeded” messages. They usually mean “SMTP accepted,” not “inbox delivered.”
Operational checklist: keep it from regressing
- When you add a new SaaS that sends mail, add it to the sender inventory and validate alignment before launch.
- Monthly: verify SPF hasn’t grown into a 10-lookup grenade.
- Quarterly: rotate DKIM keys if your provider supports it cleanly, and confirm old selectors are retired safely.
- After migrations: verify the sending path didn’t change (new IP, new relay, new provider default).
- Keep DMARC reporting mailbox monitored. Treat it as telemetry, not spam.
FAQ (the questions your future self will ask at 2 a.m.)
1) Do I need SPF, DKIM, and DMARC, or is one enough?
Use all three. SPF alone is easy to bypass and doesn’t protect the visible From header. DKIM alone can break via intermediaries. DMARC ties it together and gives you reporting.
2) Why did emails work before and now go to spam?
Because mailbox provider policies tightened, your sending IP changed, your volume pattern changed, or you added a new sender without updating SPF/DKIM. Deliverability “suddenly” failing is usually a delayed reaction to drift.
3) What’s the safest DMARC policy to start with?
p=none with aggregate reporting, then fix unknown senders. Once clean, move to quarantine, then reject. Jumping to reject without visibility is how you discover that a forgotten SaaS is still emailing invoices.
4) What does “alignment” mean in plain English?
The domain your users see in the From header must match (or be organizationally related to) the domain proven by SPF and/or DKIM. DMARC is specifically designed to enforce that relationship.
5) Should my WordPress contact form set From to the visitor’s email?
No. That causes DMARC failure for visitors whose domains enforce DMARC (which is increasingly common). Set From to your domain and set Reply-To to the visitor.
6) Can I send WordPress mail through Microsoft 365 SMTP and call it a day?
Yes, if you do it correctly: authenticated submission, correct From domain, and DKIM/DMARC set for that domain. Be careful with rate limits and application credentials; don’t use someone’s personal account.
7) Why am I seeing “SPF neutral” or “softfail”?
It usually means your SPF ends with ?all (neutral) or ~all (softfail). It can be okay during transition, but it’s weaker enforcement and may reduce trust signals. Tighten to -all when ready.
8) How do I know if my provider is signing DKIM as my domain?
Look at a received message header: find DKIM-Signature and read the d= value. It should be your domain (or your chosen sending subdomain). If it’s the provider’s domain, you’re not aligned unless your From matches that provider domain (it shouldn’t).
9) What’s the difference between Header From and Return-Path, and why should I care?
Header From is what humans see. Return-Path is where bounces go and what SPF checks. Misalignment between them is the classic DMARC fail scenario.
10) Will fixing SPF/DKIM/DMARC guarantee inbox placement?
No. It will stop you from failing basic identity checks, which is mandatory. Inbox placement also depends on reputation, content, engagement, complaint rates, and consistency.
Conclusion: practical next steps
If WordPress email is going to spam, don’t keep tweaking subject lines like you’re bribing a spam filter. Fix your sender identity.
- Decide how you will send: SMTP/API through a provider you trust, not PHP mail roulette.
- Make authentication align: SPF authorizes the real sender; DKIM signs as your domain; DMARC enforces alignment.
- Validate with evidence: headers, DNS queries, and mail logs. Not plugin screenshots.
- Enforce deliberately: DMARC
p=none→quarantine→reject, backed by real reporting and a sender inventory.
Do this once, do it right, and your password reset emails will stop taking scenic routes through spam folders.