← Back to Blog

How to Fix SPF PermError: Too Many DNS Lookups

Email · June 11, 2026 · 7 min read

Your SPF record broke silently and you didn't notice until mail started bouncing. Here is how to diagnose and fix the 10-lookup limit with flattening.

Technical cover image for How to Fix SPF PermError: Too Many DNS Lookups

The PermError You Did Not Know You Had

You published an SPF record years ago. You added a few include entries over time as the company adopted new services — Google Workspace, then Salesforce, then Mailchimp, then a support ticketing system, then a marketing automation platform. Each addition was a one-line change. None of them broke anything visibly, until one day they did.

The failure is silent. SPF does not bounce mail — it returns a result that the receiving MTA uses in its policy decision. A PermError means the SPF record could not be evaluated at all. Most receivers treat it as if no SPF record exists. DMARC then sees an SPF failure (because no pass occurred) and applies whatever policy your DMARC record specifies. If your DMARC policy is p=reject, your mail starts bouncing. If it is p=none, your mail still gets delivered — which is worse, because you never notice the SPF is broken.

This is the 10-DNS-lookup limit at work. Let us walk through diagnosing it and fixing it permanently.

Counting Your Lookups

RFC 7208 Section 4.6.4: an SPF evaluator must not issue more than 10 mechanisms that trigger DNS lookups. Every include, a, mx, ptr, and exists mechanism counts. Nested includes count too — if you include:_spf.google.com, every lookup that Google's record triggers counts against your total.

Start by pulling your current record:

dig txt example.com +short | grep spf

Now count the mechanisms that do DNS lookups manually. It is tedious and error-prone. Paste your record into the Email Authentication Checker instead. It walks the entire include tree, resolves every mechanism, and tells you exactly how many lookups your record triggers and which includes are the worst contributors.

Here is a real example of a record that breaks the limit:

v=spf1 include:_spf.google.com include:spf.protection.outlook.com
include:_spf.salesforce.com include:spf.mandrillapp.com
include:servers.mcsv.net include:_spf.createsend.com
include:spf.mtasv.net include:_netblocks.mimecast.com ~all

Eight includes looks safe. But _spf.google.com itself contains 3 include entries. spf.protection.outlook.com contains 1 include. spf.mandrillapp.com contains 1 include. When you sum them, the evaluation hits 12 to 14 lookups depending on the day. The record is broken.

Immediate Fix: Flattening

The only reliable long-term fix for lookup overflow is SPF flattening. Flattening resolves every include, a, and mx mechanism into raw ip4 and ip6 addresses. The resulting record has zero includes and always counts as exactly 1 DNS lookup.

Use the SPF Flattener on this site. Paste your SPF record, and it resolves all mechanisms down to their IPv4 and IPv6 ranges. The output looks something like:

v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20
ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16
ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17
ip4:216.58.192.0/19 ip4:216.239.32.0/19 ip4:40.92.0.0/15
ip4:40.107.0.0/16 ip4:52.100.0.0/14 ip4:104.47.0.0/17
ip4:13.111.0.0/16 ip4:136.147.0.0/16 ip4:198.245.80.0/20
ip4:205.201.128.0/20 ip4:198.2.128.0/18 ip4:148.105.0.0/16
ip4:103.237.104.0/22 -all

Publish this record and your SPF evaluation immediately drops to 1 lookup. The PermError is gone.

The Maintenance Problem

Flattening replaces a dynamic problem (includes that auto-update) with a static solution (a snapshot of IP ranges at a point in time). When Google adds a new /24 to their mail infrastructure, your flattened record does not know about it. Mail from that new range fails SPF.

You need a process. At minimum, re-run the flattener monthly. Better: script it with a cron job that dumps the flattened record, compares it against what is in DNS, and emails you on divergence. Even better: use the Email Authentication Checker in a scheduled check that alerts when your record approaches the lookup limit or when a previously included provider adds new ranges not covered by your flattened record.

Some domains take a hybrid approach: keep the most stable, slow-changing includes (like Google, which changes IPs rarely) as includes, and flatten only the noisy ones. This keeps the lookup count manageable while preserving auto-updating for the providers you trust most.

Why Not Just Use Subdomains?

You might be tempted to split your SPF across subdomains: put marketing mail on news.example.com with its own SPF, transactional on mail.example.com, and corporate on example.com. This works but requires discipline. Every subdomain needs its own SPF, DKIM, and DMARC records. The DMARC policy on example.com with sp=reject will cover subdomains, but only if you configure alignment correctly.

For most organizations, flattening is simpler. One record, one domain, no architectural changes to the mail flow.

Macro Expansion: Another Source of PermError

The 10-lookup limit is the most common cause of PermError, but not the only one. Invalid macro expansion, a record that exceeds 512 bytes (before UDP truncation), or a syntax error anywhere in the include tree also produces PermError. The Email Authentication Checker catches these too — it validates syntax across the entire evaluation chain, not just the top-level record.

If your SPF record is over 450 characters, you should also plan for UDP DNS truncation. DNS TXT records over 512 bytes trigger TCP fallback on the resolver side, and some broken resolvers do not fall back correctly. Splitting the record across multiple quoted strings in a single TXT record (DNS concatenates them) is fine, but keeping the total under 450 bytes of actual SPF data is safer.

Checking Your Fix

After publishing the flattened record, verify it resolves correctly:

dig txt example.com +short

Then run a full SPF evaluation. You can do this with spfquery from the libspf2 package:

spfquery -ip 209.85.220.41 -sender user@example.com -helo mail.example.com

Replace the IP with one from your allowed ranges. The result should be pass. Then test with an IP you know is not authorized; it should return fail (with -all) or softfail (with ~all).

Finally, send a test mail to a Gmail address and inspect the Authentication-Results header. If it shows spf=pass, you fixed it. If it still shows spf=permerror, your DNS changes have not propagated yet or the new record has a syntax problem.