An email deliverability audit is a structured check of everything between your site generating a message and a recipient’s server trusting it. Done properly it takes half an hour, and it is worth doing on any site where email matters — before a problem, not after one.
The important thing about an audit, as opposed to troubleshooting, is that it is repeatable. You are not chasing a symptom; you are establishing a baseline you can re-check later, because the single most common way deliverability breaks is that a working configuration quietly stops being correct.
Six areas. Work through them in order — each assumes the previous one passed.
1. Sending path: what is actually sending your mail?
Start here, because auditing DNS for a provider you are not using wastes the whole exercise.
Check: which mailer your site is configured to use, and what From address it actually sends with.
The From address is the one people get wrong. WordPress plugins frequently set their own — a forms plugin, WooCommerce, and your SMTP plugin can each have an opinion, and the one that wins may not be the one you configured. Send a real message and read the From address on the received copy rather than trusting the settings screen.
Pass: one clearly identified provider, sending from an address on a domain you control.
Fail: sending via PHP mail(); or a From address on a domain you do not control. Sending as a Gmail or Yahoo address fails DMARC unconditionally, because those domains publish policies telling receivers to reject mail sent on their behalf by third parties. No amount of configuration fixes it.
2. Connectivity: can your server reach the provider?
Frequently skipped, and it invalidates everything after it. Authentication is irrelevant if the message never leaves.
Check: that your server can open a connection to your mail host on the configured port.
Many hosts block outbound SMTP — port 25 nearly always, often 465 and 587 too. The signature is a test that hangs for ten to thirty seconds and then times out, as opposed to failing instantly with an authentication error. An instant rejection is credentials; a long pause then failure is a blocked port. A (0) error code means the connection never got far enough to produce a real one.
Pass: a connection opens and authenticates.
Fail: timeouts. If ports are blocked, switch to a provider’s HTTP API — API mailers use ordinary HTTPS on 443 and are unaffected.
3. SPF
Check three things, not one.
Does a record exist, and exactly one? A domain may publish only one SPF record. Two TXT records each starting v=spf1 is invalid and receivers treat it as an error, so both fail. Adding a second record for a second provider is a common and quietly fatal mistake — the mechanisms must be merged.
Does it cover your actual provider? A valid record for a provider you no longer use is worse than no record, because it looks correct.
How many DNS lookups does it require? This is the check almost everyone omits and it is the most valuable one. SPF permits ten lookups. The include, a, mx, ptr, exists and redirect mechanisms each cost one — and so does every lookup inside the records you include, which is why one include can cost four. Exceed ten and evaluation returns PermError, which DMARC treats as an SPF failure. Google Workspace plus a transactional provider plus a marketing platform commonly reaches nine to twelve.
Pass: one record, covering your provider, under ten lookups.
4. DKIM
Check: that messages are signed, and that the signature validates.
DKIM is where “I set it up and it still fails” usually originates, for a mundane reason: the records were published at the wrong DNS host. A domain registered with one company, DNS served by another, and mail handled by a third is an extremely common arrangement, and records added at the registrar do nothing when DNS is served elsewhere. Check your domain’s nameservers; whoever they point to is the only place records count.
Two panel-specific traps worth checking explicitly: many DNS panels append your domain automatically, so entering a full hostname produces selector._domainkey.example.com.example.com; and some panels save values with extra quotation marks.
Pass: dkim=pass on a real received message.
Fail: dkim=none means nothing signed the message — either DKIM was never enabled at your provider, or the published selector does not match the signing one.
5. DMARC, and alignment
Check: whether a DMARC record exists, what policy it sets, and — the part that matters — whether authentication is aligned.
This is the single most misunderstood point in deliverability, and it explains the most common complaint in the topic: “I have all three records and DMARC still fails.”
DMARC does not merely ask whether SPF or DKIM passed. It asks whether they passed for the domain in the From address the reader sees. If your store sends from [email protected] but SPF passed for your provider’s own sending domain, SPF passed and alignment failed — so DMARC fails.
The fix is domain authentication at your provider: publishing their DKIM records on your domain so signatures are made in your name. Providers usually present this as optional, and the easier path — verifying a single sender address — does not produce it.
On policy: start at p=none with a reporting address. p=none enforces nothing but makes receivers send you reports, and those reports are the only source of truth about mail claiming to be from your domain — including senders you have forgotten. Read them for a couple of weeks before moving to quarantine, and only then to reject. Publishing p=reject first is how organisations discover their own legitimate mail was failing.
6. Queue and delivery health
Not usually considered part of a deliverability audit, and it should be, because the symptom is identical from the recipient’s side.
Check: that mail is actually leaving, and that nothing is backing up.
If your site defers mail to a background queue, a stalled queue means messages sit on your own server. To the customer waiting for a password reset, that is indistinguishable from spam filtering — and no DNS check will reveal it.
On WooCommerce sites, look at WooCommerce → Status → Scheduled Actions. Pending or past-due entries accumulating means Action Scheduler is not running, and the usual cause is WP-Cron, which only fires when somebody visits the site. A real system cron every five minutes fixes it.
Also check your provider’s own dashboard, which is the most under-used diagnostic available. It shows what happened after they accepted a message — delivered, bounced, suppressed. A suppressed address explains a test that fails while real customers receive mail normally.
Reading the result
The fastest verification for areas 3 to 5 is a single real delivery. Send to a Gmail address, open the message, choose Show original, and read Authentication-Results:
spf=pass dkim=pass dmarc=pass
This is better than any testing tool, because it is what a receiving server actually concluded rather than what a tool predicts.
If all three pass and mail still lands in spam, the audit is complete and the remaining problem is reputation or content — not configuration. Continuing to edit DNS at that point is the most common way to waste a day.
A worked example
What a real audit turns up on an ordinary store, to make the process concrete.
Sending path. Configured for a transactional provider. But the received From address is [email protected] rather than the configured [email protected] — a forms plugin is overriding it. Two identities, one authenticated. Finding.
Connectivity. Connects and authenticates. Pass.
SPF. One record, correct syntax. Lookup count is nine — under the limit but with almost no headroom, and it includes a former email provider that has not been used since a migration. Finding: prune it now, because the next service added will break authentication.
DKIM. Passes, signed by the store’s own domain. Pass.
DMARC. Record exists at p=none, published two years ago, with a reporting address pointing at a former employee. So it enforces nothing and nobody reads the reports. Finding.
Queue. Empty. But Scheduled Actions shows past-due entries from three weeks ago, meaning cron ran late during a quiet period. Finding: WP-Cron on a low-traffic site.
Four findings, none of which had produced a complaint yet, and all of which would have — the SPF headroom in particular is a scheduled failure waiting for the next marketing tool somebody adds. That is the argument for auditing before there is a problem.
Auditing what your site sends, not just how
The checks above cover the sending path. One area sits outside them and is worth a pass while you are here: the set of messages your site actually generates.
Most WordPress sites send more mail than their owners realise, from more sources. A quick inventory usually turns up:
- WordPress core — password resets, new user registration, admin email change confirmations, and automatic update notifications.
- WooCommerce — a dozen order and account emails, each individually toggleable.
- Forms plugins — notifications and autoresponders, frequently with their own From address.
- Security and backup plugins — alerts that go to an administrator address which may no longer be monitored.
- Anything sending to a departed employee’s address.
Two findings recur. First, notifications going to an address nobody reads, which means failures have been invisible for months. Second, plugins overriding the From address, which is how one site ends up sending from three identities with only one of them authenticated.
Checking the From address on a received message from each source is more revealing than any settings screen, because the settings screen shows intent and the header shows what happened.
How this differs from troubleshooting
Worth being explicit, because the two get conflated and the difference determines whether the work is worth repeating.
Troubleshooting starts from a symptom and stops when the symptom goes. It is reactive, it is scoped to whatever broke, and it leaves you knowing nothing about the areas you did not touch.
An audit starts from a checklist and stops when every area has a recorded result. It runs when nothing is wrong. Its output is a baseline rather than a fix, and its value is almost entirely in the comparison you make against it later.
The practical consequence: an audit that finds nothing is not wasted work, which is the opposite of how troubleshooting feels. It is the run that establishes what “correct” looked like on a known date — and that is what turns the next failure from an investigation into a diff.
What to record
An audit you cannot compare against is just troubleshooting. Write down the values, not the verdicts — “SPF passed” is useless in three months, whereas the record itself tells you what changed.
Worth capturing, per domain:
- The full SPF record as published, and its lookup count.
- The DKIM selector in use, and which provider it belongs to.
- The DMARC policy, and the reporting address.
- The configured mailer, host and port.
- The From address actually observed on a received message — not the configured one.
- The date, and who ran it.
When mail breaks six months later, this turns “something changed” into a diff. In practice the culprit is usually visible in seconds: a selector that no longer matches, a lookup count that has crept from eight to eleven, or a From address a plugin update quietly altered.
Auditing a network or a client portfolio
Doing this once is straightforward. Doing it across forty client sites is where it falls apart, and the failure is always the same — it gets done at handover and never again.
Three things make it tractable:
Audit the domain, not the site. SPF, DKIM and DMARC are properties of a domain. If a client runs three sites on one domain, that is one DNS audit and three sending-path checks, not three full audits.
Enforce settings centrally where you can. On multisite, settings that resolve from the network level cannot drift per-site. NimblyMail resolves configuration in a defined order — PHP constant, then network lock, then site override, then network default, then plugin default — so a network administrator can lock the From address and mailer and remove a whole category of drift. This is in the free plugin.
Pin credentials in configuration, not the database. Settings defined as PHP constants outrank the database, which means a production database restored onto staging cannot carry live sending credentials with it. That single change removes the most common cause of staging sites emailing real customers.
Common audit findings, ranked by how often they appear
| Finding | Why it happens | Effort to fix |
|---|---|---|
| DMARC fails on alignment | Provider domain authentication never completed | Low — publish the provider’s records |
| Records at the wrong DNS host | Registrar and nameservers are different companies | Low — republish in the right place |
| SPF over ten lookups | Accumulated providers, nested includes | Medium — prune, then flatten |
| Two SPF records | A second provider was added as a new record | Low — merge into one |
| From address on a free-mail domain | Set up before anyone considered DMARC | Low — but requires a decision |
| No DMARC record at all | Nobody knew it was needed | Low — start at p=none |
| Queue not draining | WP-Cron on a low-traffic site | Medium — needs a real cron |
| Host blocks outbound SMTP | Shared hosting policy | Low — switch to an API mailer |
The first two account for a large share of everything found, and both are dull. That is characteristic of this topic: the failures are rarely sophisticated, they are just invisible.
The part that makes an audit worth anything
Everything above is a snapshot. The uncomfortable truth is that a passing audit has a shelf life.
Records get edited during an unrelated migration. Providers rotate DKIM keys. A host changes its firewall. An OAuth token expires — Gmail apps left in “Testing” publishing status have refresh tokens that die after seven days. A plugin update changes a From address. Someone adds a second SPF record instead of merging.
None of it announces itself. Mail keeps being generated, and quietly stops being trusted.
So the last step is the one that determines whether the previous six were worth doing: schedule the re-check. Quarterly by hand is the usual recommendation and it is better than nothing, though a quarter is a long time to be sending unauthenticated mail.
NimblyMail runs this as six pillars — SPF, DKIM, DMARC, PTR, connectivity and queue health — weekly, with an on-demand re-run. Two things make it more useful than a generic external check:
- It knows your provider. The SPF
includeit expects and the DKIM selector it looks for are read from the mailer you actually configured, so it names the specific record you are missing instead of reporting a generic SPF warning. - Connectivity is a real connection. It opens an actual TCP socket to your SMTP host and port with a five-second timeout, or issues an HTTP request for API mailers, and reports which endpoint passed or failed. An external tool cannot test this, because the question is whether your server can reach the provider.
Each pillar returns pass, warning or failure with a plain-language reason, and pillars that do not apply are reported as skipped rather than silently omitted.
Checklist
| Area | Pass looks like | Most common failure |
|---|---|---|
| Sending path | One provider, own domain in From | Free-mail From address |
| Connectivity | Connection opens and authenticates | Host blocks outbound SMTP ports |
| SPF | One record, covers provider, under 10 lookups | Over the lookup limit, or two records |
| DKIM | dkim=pass on a real delivery |
Records at the wrong DNS host |
| DMARC | dmarc=pass, aligned, reporting on |
Alignment, not the record itself |
| Queue | Nothing pending or past due | WP-Cron not firing on a quiet site |
