How to fix SPF “too many DNS lookups” (PermError)

SPF is only allowed to trigger 10 DNS lookups while a receiver evaluates it. Every include, a, mx, ptr, exists and redirect costs at least one — and includes nest, so a handful of providers can quietly blow past ten. When they do, SPF returns PermError, which most receivers treat as a hard failure that also breaks DMARC. The fix is to count your lookups, consolidate, and flatten only the parts that are safe to freeze.

Written & reviewed by the VerifyAnyEmail team · Last reviewed July 2026

The error sounds obscure but the cause is mundane: you added one email provider too many. SPF (RFC 7208, §4.6.4) caps the number of DNS-querying mechanisms a receiver will resolve at ten. Cross that line and evaluation stops with a permerror — SPF doesn’t “mostly work,” it fails outright. Because a broken SPF result can’t align, this also drags down DMARC. This guide shows exactly what counts toward the ten, why nested includes are the usual culprit, and how to get back under the limit without creating a fragile record.

The 10-lookup rule, precisely

The limit counts mechanisms that require a DNS query, evaluated recursively across every include. These cost a lookup each: include, a, mx, ptr, exists, and redirect. These do not cost a lookup: ip4, ip6, and all — they’re static, so use them freely. Two subtleties bite people:

  • Includes nest. When you include:_spf.google.com, that record itself contains more includes, and every one of those counts too. A single top-level include can consume three or four of your ten before you’ve added anything else.
  • An mx mechanism can cost more than one. RFC 7208 also caps the number of MX records resolved at 10 per mx lookup, and each name still resolves — a domain with many MX hosts is expensive.

The limit is per evaluation on the receiver’s side, not a count of lines in your record. That’s why a short-looking record can still PermError — the cost is hidden inside the includes.

A record that’s over budget

This looks reasonable — six providers, plus a and mx — but it’s well past ten once the includes expand:

v=spf1 include:_spf.google.com include:spf.protection.outlook.com
  include:sendgrid.net include:_spf.salesforce.com
  include:mail.zendesk.com include:servers.mcsv.net
  a mx ~all

Counting the nested total: Google’s include alone expands to several, Outlook’s adds more, and each of SendGrid, Salesforce, Zendesk and Mailchimp contributes one or more — plus the a and the mx. The receiver stops at the tenth lookup and returns PermError; every mechanism after that point is never even read.

What PermError does to your mail

Most receivers treat permerror as equivalent to a hard SPF failure. In the Authentication-Results header it appears like this:

Authentication-Results: mx.google.com;
  spf=permerror (google.com: too many DNS lookups) smtp.mailfrom=news@acme.com;
  dmarc=fail (p=REJECT dis=REJECT) header.from=acme.com

SPF didn’t just fail to pass — it errored, so there is no aligned SPF identifier for DMARC to lean on. If DKIM is also unaligned, DMARC fails, and under p=reject the message is refused. Worse, this is silent: nothing in your sending platform warns you. You find out from bounces or from reading a header, which is why periodic checking matters. To decode that header field by field, see how to read the Authentication-Results header.

Step 1 — count and see the tree

Before changing anything, measure. The SPF Include Tree expands every nested include and shows you the running lookup count, so you can see exactly which provider is the expensive one — Google and Microsoft are frequently the biggest single contributors. Pair it with the SPF Checker to confirm the current pass/fail and total. You can’t consolidate what you haven’t measured.

Step 2 — consolidate the safe way

Work through these in order; each is low-risk and often enough on its own:

  • Remove senders you no longer use. Old ESPs, a CRM you migrated off, a form tool from a campaign two years ago — every stale include is a free lookup back. This is the single highest-value cleanup.
  • Drop redundant a and mx. These are often left over from a default template. You only need mx if your MX hosts actually send mail; many domains receive on their MX but send through an ESP, making the mx mechanism pure waste.
  • Replace an a/mx with an explicit ip4/ip6 when the address is stable. Static IP mechanisms cost zero lookups.
  • Move a whole sending stream to a subdomain. Send marketing from news.acme.com with its own SPF record and your root domain’s record shrinks. This also cleanly separates transactional and marketing mail.

Very often, removing two dead includes and a needless mx drops you comfortably under ten and you’re done — no flattening required.

Step 3 — flatten, carefully, only if you must

“Flattening” means replacing an include: with the actual ip4/ip6 ranges it currently resolves to, so those addresses cost no lookups. It reliably fixes the count — but it trades a maintenance burden you didn’t have before, and the risks are real:

  • Provider IPs change without notice. When Google or SendGrid adds or retires a sending range, their include updates automatically — your flattened copy does not. Stale ranges mean legitimate mail suddenly SPF-fails.
  • Records have length limits. A single TXT string maxes at 255 characters and the whole record at 512 bytes over UDP; dumping dozens of ranges inline can overflow and create new problems.
  • You inherit an operational job. A flattened record must be re-checked on a schedule, effectively forever. If you flatten, prefer flattening only stable senders (your own static IPs, a fixed relay) and leave volatile provider includes as real includes.

A pragmatic hybrid record keeps the big, well-maintained provider include intact while flattening your own known-stable IPs:

v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.0/24
  ip4:203.0.113.16/28 include:_spf.google.com ~all

After any change, re-run the SPF Include Tree to confirm the total is at or below ten, and the SPF Checker to confirm it still passes for your real sending IPs.

Also watch: two other SPF traps

  • Only one SPF record allowed. Two v=spf1 TXT records on the same domain is itself a PermError. Merge every sender into one record — never publish a second.
  • The void lookup limit. RFC 7208 also caps lookups that return no answer (NXDOMAIN / empty) at two; includes pointing at dead hostnames burn that budget and error out. Remove includes that no longer resolve.

Verify and move on

Once you’re back under ten and SPF passes, confirm the whole authentication picture: run the DMARC Checker to make sure alignment now holds, and the DKIM Checker so DKIM can back up SPF on forwarded mail. If SPF passes but DMARC still fails, the issue is alignment, not lookups — see why SPF passes but DMARC fails. For the complete model of all three records, read the complete SPF, DKIM & DMARC guide, and once everything is clean, tighten policy with how to move DMARC to p=reject safely. The authoritative source for every rule above is RFC 7208; Google’s SPF limits guidance and Microsoft’s SPF configuration docs cover the provider-specific includes.

Check your domain in seconds

Run the free diagnostics referenced in this guide — no sign-up needed.