How to check SPF, DKIM and DMARC records for a domain (and fix “too many DNS lookups”)
Short version. SPF lives in a TXT record on the domain itself, DMARC in a TXT record at _dmarc. plus the domain, and DKIM at selector._domainkey. plus the domain. You can read all three with dig, nslookup, or our free in-browser checker. The most common hidden failure is an SPF record that needs more than 10 DNS lookups, which makes SPF return permerror for every message.
Why these three records matter now
SPF, DKIM and DMARC are how a receiving mail server decides whether a message that claims to be from your domain really is. Since February 2024, Google and Yahoo have required every sender to have SPF or DKIM, and bulk senders (roughly 5,000+ messages a day to their users) to have SPF, DKIM and a DMARC record. Microsoft announced similar requirements for high-volume senders to Outlook.com addresses in 2025. Even if you send far less, a broken record is one of the fastest ways to land in spam.
The records also break quietly. Someone adds a new sending tool to SPF, a provider rotates a DKIM key, or a DNS migration drops the _dmarc record, and nothing tells you. That is why it pays to know how to check them and, ideally, to check them on a schedule.
One standards note: in May 2026 the IETF published the updated DMARC specification as RFC 9989 (with RFC 9990 and 9991 for reporting), replacing RFC 7489. Existing records keep working; the main visible change is that the pct= tag is gone in favour of a simpler t=y “testing” flag.
1. Check the SPF record
SPF is a TXT record on the domain that starts with v=spf1. On macOS or Linux:
dig +short TXT example.com | grep -i "v=spf1"
On Windows (Command Prompt or PowerShell):
nslookup -type=txt example.com
A typical result looks like this:
"v=spf1 include:_spf.google.com include:sendgrid.net ~all"
How to read it
| Part | Meaning |
|---|---|
v=spf1 | Marks the TXT record as SPF. There must be exactly one such record. Two SPF records is a permerror. |
ip4: / ip6: | Allows a specific address or range. Costs no DNS lookups. |
include: | Pulls in another domain's SPF record (for example your email provider's). Costs one lookup, plus every lookup inside that record. |
a, mx, exists, ptr, redirect= | Also cost one lookup each. ptr is discouraged by the SPF spec. |
-all | Fail anything not listed (strictest). |
~all | Soft-fail anything not listed. A common, safe choice while DMARC does the enforcing. |
?all | Neutral: says nothing. Weak; treat it as “no policy”. |
+all | Allows any server on the internet to pass SPF for your domain. Always a mistake. |
Remember that SPF checks the envelope sender (the Return-Path / bounce domain), not the From address people see. A marketing tool that sends with its own bounce domain does not need to be in your root SPF record at all; adding it just burns lookups.
2. Fix “SPF PermError: too many DNS lookups”
RFC 7208 section 4.6.4 caps SPF evaluation at 10 DNS-querying terms, counted across every nested include. A receiver that needs an 11th returns permerror, and with a DMARC policy in place that can mean rejection or the spam folder. The spec also recommends failing after more than 2 “void” lookups (names that return nothing), which is what you get from includes for services you stopped using.
Count your lookups
You cannot count by eye, because each include hides its own includes. Expand them one level at a time:
dig +short TXT _spf.google.com
# "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"
That single include:_spf.google.com already costs 4 lookups (itself plus three nested includes). Our free SPF/DMARC checker walks the whole tree in your browser and shows the running total.
Ways to get back under 10, in order of preference
- Delete includes you no longer use. Old CRMs, a help desk you replaced, a newsletter tool from two years ago. This is the answer more often than not.
- Remove duplicates and unnecessary
a/mx. If your mail provider's include already covers your MX hosts,mxis a wasted lookup. - Move a sender to a subdomain. Send marketing mail from
news.example.comwith its own SPF record; it gets its own budget of 10. - Use the provider's own bounce domain. Many ESPs let you use a custom Return-Path CNAME, which means their SPF lives on that hostname, not your root.
- Flatten only as a last resort. Replacing includes with literal
ip4:ranges removes lookups, but those ranges change without notice. If you flatten, you must re-check regularly.
After you change DNS, allow for the record's TTL (often minutes, sometimes up to 48 hours) before testing again.
3. Check DKIM (and find the selector)
DKIM keys are published at <selector>._domainkey.<domain>. The catch is that DNS has no “list all selectors” query, so you need the selector name first.
Find the selector
Open any email your domain sent, view the original message / headers, and find the DKIM-Signature header. The s= tag is the selector and d= is the signing domain:
DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=google; ...
Selectors you will often see (not guaranteed; providers let you change them): google (Google Workspace default), selector1 and selector2 (Microsoft 365), k1 (Mailchimp), s1/s2 (SendGrid), and generic names like default or dkim.
Look up the key
dig +short TXT google._domainkey.example.com
# "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
- No answer at all: wrong selector, or the key was never published.
p=with nothing after it: the key has been revoked. Mail still signed with that selector will fail DKIM.- A short
p=value (roughly 216 base64 characters) usually means a 1024-bit RSA key. Most providers now recommend 2048-bit. - Microsoft 365 and several ESPs publish DKIM as a CNAME that points to their DNS.
digfollows it automatically; if the CNAME target is missing, DKIM fails.
4. Check the DMARC record
dig +short TXT _dmarc.example.com
# "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; adkim=r; aspf=r"
If there is no record on a subdomain, receivers fall back to the organisational domain's record (for mail.example.com, that is _dmarc.example.com).
| Tag | What to look for |
|---|---|
p= | none = monitor only (nothing is blocked). quarantine = failing mail goes to spam. reject = failing mail is refused. Start at none, read the reports, then tighten. |
rua= | Where aggregate reports go. Without it you are flying blind, because you never see who is sending as you. |
sp= / np= | Policy for subdomains / for non-existent subdomains (np is newer). Missing means “same as p”. |
adkim= / aspf= | Alignment mode: r relaxed (default) or s strict. |
pct= | Legacy: percentage of failing mail the policy applies to. Removed in RFC 9989; receivers may ignore it. Use t=y if you want “testing mode”. |
DMARC passes when SPF or DKIM passes and the passing domain aligns with the visible From domain. That is why a correct SPF record alone is not enough: a newsletter sent through a third party with its own bounce domain will only pass DMARC if it is DKIM-signed with your domain.
5. A quick health checklist
- Exactly one
v=spf1record, ending in~allor-all(never+allor?all). - SPF evaluation uses 10 DNS lookups or fewer, with no includes that resolve to nothing.
- Every service that sends as your domain signs with DKIM using your domain (
d=), with keys that are not revoked, preferably 2048-bit. - A
_dmarcrecord with arua=address, moving fromp=nonetowardsquarantineorrejectonce reports look clean. - Parked domains that never send mail:
v=spf1 -allandv=DMARC1; p=reject, so nobody can spoof them.
6. Monitor for changes automatically
A one-off check tells you today's state. Problems usually appear later: a teammate edits DNS, a vendor's include grows past the lookup limit, or a key is rotated. If you manage more than one or two domains (an agency, an MSP, a cold-email team), a daily automated check that only alerts on changes saves a lot of manual work.
If you already use n8n, we publish a free workflow template that does exactly this. It checks a list of domains every day for the issues in the checklist above, keeps a ledger of each finding (new, changed, fixed or still open), emails a digest, and posts to Slack only when something changes.
Free: SPF, DKIM & DMARC Monitor (n8n template)
Daily multi-domain checks, change-only Slack alerts and an HTML email digest. Pay what you want, $0 is fine.
Get the free template Check one domain nowDisclosure: the template runs the checks through an Apify Actor that EdenWorks built and publishes. You pay Apify's usage price (about $0.002 per domain per day, and Apify's free plan credits cover light use), and EdenWorks earns part of that fee. You can do every check on this page by hand for free.
Related guides
- How to check SSL certificate expiry and monitor it (before certificates shrink to 47 days)
- How to find broken links on your website, free, and get a weekly report
Sources: RFC 7208 (SPF), RFC 6376 (DKIM), RFC 9989 (DMARC); Google's email sender guidelines. This guide is general information, not a guarantee of inbox placement.