"SPF PermError: too many DNS lookups" — Causes & Fix

Your SPF record needs more than 10 DNS lookups to evaluate, so receivers return a permanent error (spf=permerror). Here is why it happens and how to get back under the limit.


"SPF PermError: too many DNS lookups" means your SPF record requires more than 10 DNS lookups to fully evaluate. RFC 7208 caps SPF at 10 lookups for mechanisms like include, a, mx, ptr, exists, and redirect. Cross that limit and the receiver stops evaluating and returns a permanent error (spf=permerror) — which most providers treat as an SPF failure. The fix is to reduce the number of nested lookups: remove senders you no longer use, replace bloated include: chains with the IP ranges they resolve to, and avoid mechanisms that trigger extra lookups.

What the Error Looks Like

The error surfaces in the Authentication-Results header of a delivered or bounced message, and sometimes directly in an SMTP rejection:

text
# In the message headers Authentication-Results: mx.google.com; spf=permerror (google.com: permanent error in processing during lookup of user@example.com: DNS problem) smtp.mailfrom=example.com; dmarc=fail (p=REJECT sp=REJECT dis=REJECT) header.from=example.com # Some receivers word it explicitly Received-SPF: permerror (too many DNS lookups) client-ip=203.0.113.9; # A bounce that references the permerror 550 5.7.23 The message was rejected because of Sender Policy Framework violation

Because DMARC needs SPF (or DKIM) to pass and align, an SPF permerror often cascades into a DMARC failure too — so the same broken record can bounce mail on two checks at once.

Count your SPF lookups in the SPF Validator

Why the 10-Lookup Limit Exists

Every include: in an SPF record points at another domain whose SPF record must also be fetched — and that record can contain more includes, and so on. Without a cap, a single message could trigger dozens of recursive DNS queries, turning SPF into a denial-of-service amplifier. RFC 7208 (§4.6.4) therefore limits evaluation to 10 DNS-querying mechanisms. Exceed it and the result is permerror — a permanent, not temporary, failure.

What counts toward the limit

  • include: — each one is 1 lookup, plus every lookup inside the record it points to.
  • a and mx — 1 lookup each (and mx costs an extra lookup per MX host it resolves).
  • ptr — 1 lookup, and it is discouraged by the RFC; avoid it entirely.
  • exists: — 1 lookup.
  • redirect= — 1 lookup for the record it redirects to.

What does NOT count

  • ip4: and ip6: — matched from the record itself, zero DNS lookups.
  • all (~all, -all) — the terminal mechanism, no lookup.
ip4/ip6 are free — this is the key to fixing permerrorBecause literal IP mechanisms cost nothing, replacing a heavy include: with the IP ranges it resolves to ("flattening") is the most direct way to drop back under 10 lookups.

Why It Happens

1. Too many third-party senders

Each service you add — Google Workspace, Microsoft 365, SendGrid, Mailchimp, a CRM, a helpdesk — brings its own include:. A handful of vendors quietly pushes a record past 10, especially when several of them nest their own includes.

2. A single include that is itself huge

Some provider includes expand into many nested lookups on their own. A well-known example is include:_spf.google.com, which fans out into several sub-includes — one entry in your record can silently consume 3–4 of your 10 lookups.

3. Leftover includes for services you no longer use

SPF records tend to accumulate. Old newsletter tools, a migrated helpdesk, a retired marketing platform — the include: stays in the record long after the service is gone, burning lookups for nothing.

4. Using a, mx, or ptr mechanisms

mx looks convenient but costs a lookup for the record plus one per MX host, and ptr is both a lookup and explicitly discouraged. These add up fast on top of your includes.

permerror is not temperrorA temperror means a transient DNS problem — retrying may succeed. A permerror means the record itself is invalid (too many lookups, a syntax error, or multiple SPF records) and will fail every time until you fix it. Don't wait it out — it will not resolve on its own.

How to Diagnose It

First, look at your published record and count the lookup-causing mechanisms. Remember that each include: can hide more lookups inside it:

bash
# Your SPF record — count include/a/mx/ptr/exists/redirect dig TXT example.com +short | grep spf1 # e.g. "v=spf1 include:_spf.google.com include:sendgrid.net include:servers.mcsv.net a mx ~all" # Expand a heavy include to see how many nested lookups it costs dig TXT _spf.google.com +short dig TXT _netblocks.google.com +short
See the exact lookup count per mechanismThe SPF Validator expands every include and tells you which one tips you over the 10-lookup limit.
Validate SPF now

How to Fix It

Fix A — Remove senders you no longer use

The cheapest fix is deletion. Audit every include: against the services you actually send from today, and drop the ones for retired tools. Often this alone brings the record back under 10.

text
; Before — includes for two services you no longer use example.com. 3600 IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net include:old-crm.net include:legacy-mailer.com a mx ~all" ; After — only current senders remain example.com. 3600 IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"

Fix B — Drop a, mx, and ptr

Replace mx and a with the actual IPs of your mail servers using ip4:/ip6:, which cost no lookups. Remove ptr entirely — it is discouraged and rarely needed.

text
; Before — a + mx add several lookups example.com. 3600 IN TXT "v=spf1 a mx include:sendgrid.net ~all" ; After — pin the IPs directly, zero lookups for them example.com. 3600 IN TXT "v=spf1 ip4:203.0.113.9 ip4:198.51.100.0/24 include:sendgrid.net ~all"

Fix C — Flatten a heavy include to its IP ranges

If one provider consumes several nested lookups, resolve its published ranges and inline them as ip4:/ip6: mechanisms. This is the most powerful reduction, but it has a trade-off — see the warning below.

text
; Instead of include:_spf.google.com (which nests 3-4 lookups), ; inline the IP blocks it resolves to (zero lookups): example.com. 3600 IN TXT "v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip6:2001:4860:4000::/36 include:sendgrid.net ~all"
Flattened records go staleWhen you inline a provider's IP ranges, you no longer track their changes automatically. If the provider adds or retires sending IPs, your SPF becomes wrong and mail starts failing. Only flatten providers whose ranges are stable, and set a reminder to re-check them — or use a managed SPF-flattening service that keeps them current.

Fix D — Delegate a subdomain for bulk mail

Move high-volume marketing or transactional mail to a subdomain (e.g. mail.example.com) with its own SPF record. The subdomain gets its own fresh 10-lookup budget, keeping your primary domain lean. Keep DMARC alignment relaxed (the default) so the organizational domains still match.

text
; Bulk sender lives on its own subdomain with a separate budget mail.example.com. 3600 IN TXT "v=spf1 include:sendgrid.net include:servers.mcsv.net ~all" ; Root domain stays small — just your primary mailbox provider example.com. 3600 IN TXT "v=spf1 include:_spf.google.com ~all"
One SPF record onlyA domain must publish exactly one SPF TXT record. Two v=spf1 records is itself a permerror. If you are consolidating senders, merge them into a single record rather than adding a second one.

Verify the Fix

Confirm you are under 10 lookups before you resendRe-run the validator — the lookup count should read 10 or fewer with no permerror.
Verify SPF now

DNS changes only take effect once the old record's TTL expires. After publishing, confirm the new record is live, send a test message, and check that its headers now show spf=pass instead of permerror:

bash
# Confirm the updated record is live dig TXT example.com +short | grep spf1 # Send a test to a mailbox you control, then in the raw headers look for: # spf=pass (google.com: domain of user@example.com designates 203.0.113.9 # as permitted sender) smtp.mailfrom=example.com

You can also send to a Gmail address and use Show original, or paste the full source into the Email Header Analyzer to confirm the SPF verdict.

Frequently Asked Questions

Can I just raise the limit above 10?

No. The 10-lookup limit is fixed by RFC 7208 and enforced by the receiving server, not by your DNS. You cannot configure your way around it — you have to reduce the number of lookups your record requires.

Does include:_spf.google.com really cost more than one lookup?

Yes. That one include expands into several nested includes (its own IPv4 and IPv6 netblocks), typically consuming 3–4 of your 10 lookups by itself. It is a common reason a record with only a few vendors still trips the limit.

Is flattening my SPF record safe?

It works, but it freezes the provider's IP ranges at the moment you inline them. If the provider later changes their sending IPs, your SPF becomes inaccurate and mail can fail. Only flatten stable ranges, and re-check them periodically or use a service that keeps flattened records updated.

Why did permerror also break my DMARC?

DMARC passes only if SPF or DKIM both passes and aligns. An SPF permerror is not a pass, so unless DKIM is passing and aligned, DMARC fails as well. Fixing the lookup count usually clears both.

Do ip4: entries count toward the limit?

No. ip4: and ip6: are matched directly from the record with no DNS query, which is exactly why replacing includes with IP ranges reduces your lookup count.

Related