How to fix SPF "too many DNS lookups" (PermError)
October 10, 2026 · 7 min read
If a DMARC report, a mail tester or a bounce message says your SPF record returned PermError or "too many DNS lookups", receiving servers are treating your SPF as broken. For many of them that means your mail fails SPF entirely, and with a strict DMARC policy it can land in spam or be rejected. The cause is almost always the same: every service you added to the record pushed it over a hard limit of 10 lookups.
The rule: 10 lookups, counted recursively
SPF (RFC 7208) allows at most 10 mechanisms that need a DNS lookup while evaluating one record. These count toward the limit:
include:— and everything inside the included record counts tooaandmxexists:,ptr(deprecated; avoid it) and theredirect=modifier
These don't count: ip4:, ip6: and all. There is also a second, lesser-known limit: no more than two "void" lookups, meaning lookups that return nothing, such as an include: for a domain that no longer publishes SPF.
The trap is nesting. include:_spf.google.com looks like one lookup, but Google's record includes three more, so it costs four. Add Microsoft 365, a CRM, a newsletter tool and a helpdesk and you are past 10 without noticing.
Count your lookups
Start with your record:
dig +short TXT example.com | grep spf1
"v=spf1 include:_spf.google.com include:spf.protection.outlook.com include:servers.mcsv.net include:sendgrid.net include:mail.zendesk.com mx ~all"Then follow every include: the same way and add up every lookup mechanism you find, at every level. For the record above:
| Mechanism | Lookups |
|---|---|
include:_spf.google.com | 4 (1 + three nested includes) |
include:spf.protection.outlook.com | 1 or more, depending on Microsoft's current record |
include:servers.mcsv.net | 1+ |
include:sendgrid.net | 1+ |
include:mail.zendesk.com | 1+ |
mx | 1 |
| Total | 9 at best — one more service and it breaks |
Providers change their own records without telling you, so a record that was at 9 last month can be at 11 today. That is why this error tends to appear suddenly on a domain nobody touched.
Fix 1: remove what you no longer use
Every include: should map to something that sends mail as your domain today. Old newsletter platforms, a CRM you cancelled, a previous mail host after a migration: delete them. Also check whether mx and a are needed. If your MX hosts are Google or Microsoft, their include already covers them, and mx is a wasted lookup.
Fix 2: move bulk senders to a subdomain
SPF checks the domain in the envelope sender (the bounce address), not the visible From address. Most marketing and transactional services let you use a custom bounce or "return-path" domain such as bounce.example.com. Give each service its own subdomain with its own SPF record, and your main record only needs to cover the services that send with your root domain. DMARC still passes as long as alignment is relaxed (the default), because the subdomain belongs to your organizational domain.
Fix 3: replace stable hosts with IP addresses
If you send from your own servers with fixed addresses, list them as ip4: or ip6: instead of a or an include. Those cost nothing. Only do this for addresses you control; never copy a provider's IP ranges by hand.
Fix 4: flatten — carefully
"Flattening" replaces includes with the IP ranges they currently resolve to, which can take a record from 14 lookups to 1. The risk is drift: when Google or Microsoft add a range, your flattened record doesn't, and mail from the new range starts failing SPF. If you flatten, flatten only the providers that push you over, keep a note of what each block came from, and re-check regularly.
After the fix
- Re-count: you want headroom, ideally 8 or fewer, so the next provider change doesn't break you.
- Keep exactly one SPF record per domain; two
v=spf1records is also a PermError. - Watch your DMARC aggregate reports for a week to confirm SPF passes for every service you kept.
Let your Mac keep count
Postcheck counts SPF lookups recursively for every domain you add, flags void lookups and duplicate records, and re-checks every six hours so you hear about drift before your mail does. When a record is over the limit, its SPF flattener builds a replacement for you and warns you when the flattened ranges drift from the providers' current ones. It runs entirely on your Mac with DNS over HTTPS; there's no account and nothing is sent to us.