Inside The SPF 10-Lookup Limit
The Mythic Intel Team · May 12, 2026 · 7 min read
SPF fails with a permerror when evaluating your record requires more than 10 DNS-querying mechanisms. RFC 7208 caps the count at 10 terms that trigger DNS resolution, and a receiver that crosses that ceiling stops evaluating and returns permerror instead of pass or fail. The limit is per evaluation, not per record, so every include you nest counts its own lookups toward the same budget of 10.
This is the single most common reason a correctly authorized sender still fails authentication. Your IPs are all listed, your syntax is clean, and the record still breaks because you crossed a counting limit most people never see until a DMARC report shows spf=permerror.
What RFC 7208 Actually Says
Section 4.6.4 is exact: "SPF implementations MUST limit the total number of those terms to 10 during SPF evaluation, to avoid unreasonable load on the DNS." The terms that count are the DNS-querying ones: include, a, mx, ptr, exists, and the redirect modifier.
The mechanisms that resolve to nothing at evaluation time do not count. The RFC names them: "The other terms, the 'all', 'ip4', and 'ip6' mechanisms, and the 'exp' modifier, do not cause DNS queries at the time of SPF evaluation." So you can list a hundred ip4 blocks and spend zero of your budget. The cost is in the indirection, not the address count.
Which Mechanisms Burn A Lookup
include:_spf.example.comcosts 1 lookup to fetch that record, plus every DNS-querying mechanism inside it, recursively. A singleincludefor a large provider routinely pulls in 3 to 6 nested lookups.aandmxcost 1 lookup each. Formx, there is a second sub-limit: "the evaluation of each 'MX' record MUST NOT result in querying more than 10 address records, either 'A' or 'AAAA' resource records." Ten MX hosts, each with an A record, is fine. An MX host that fans out to more than 10 address records is itself a permerror.ptrcosts lookups and is actively discouraged. RFC 7208 says it "is slow, it is not as reliable as other mechanisms in cases of DNS errors, and it places a large burden on the .arpa name servers," and that it "SHOULD NOT be published."existscosts 1 lookup per evaluation.redirect=costs 1 lookup plus whatever the target record spends.
The Second Limit Nobody Counts: Void Lookups
There is a parallel ceiling that fails records well under 10 total lookups. RFC 7208 says SPF systems "SHOULD limit 'void lookups' to two." A void lookup is a DNS query that returns NXDOMAIN or zero answer records. The third void lookup returns permerror.
This bites when a vendor decommissions a subdomain that your include still references, or when an a/mx target no longer resolves. Your total mechanism count can be 6 and you still get permerror because three of those resolved to nothing. Void lookups are the reason a record that passed last quarter quietly breaks this quarter with no edit on your side.
A Real Record And Its Cost
example.com. IN TXT "v=spf1 ip4:198.51.100.0/24 include:_spf.google.com include:sendgrid.net include:servers.mcsv.net a:mail.example.com -all"
Counting the budget:
ip4:198.51.100.0/24-> 0include:_spf.google.com-> 1, and that record contains three nestedincludemechanisms -> +3 = 4include:sendgrid.net-> 1 -> 5include:servers.mcsv.net-> 1, with its own nested includes -> often +2 = 8a:mail.example.com-> 1 -> 9
Nine. Add one more vendor and you are at permerror. The -all at the end costs nothing; it only sets the policy for non-matching senders.
Flattening, And Why It Is A Tradeoff
Flattening resolves every include down to its underlying ip4 and ip6 blocks so the published record contains only addresses, which count as zero lookups. It is the only way to authorize many vendors under a hard cap.
The cost is that you have frozen another company's infrastructure into your DNS. When a provider adds a sending range, your flattened record does not know, and mail from the new IP fails SPF until you re-flatten. You also lose the human-readable map of which IP belongs to which vendor. If you flatten, automate the re-resolution on a schedule and alert on drift; a static hand-flattened record is a deliverability incident waiting for the vendor's next IP change.
The lighter-touch alternatives are real: consolidate vendors so you publish fewer includes, move low-volume mail streams to a subdomain with its own SPF record and its own budget of 10, and drop any ptr or stale include you find. The exists macro can also return a per-sender answer dynamically, which keeps the published record small.
How To Diagnose It Fast
Pull the record and count the DNS-querying terms by hand, then expand each include recursively. Any SPF evaluator will report the running total and flag the first term that pushes past 10, plus void lookups separately. In aggregate DMARC reports, the signal is spf=permerror on messages you know you sent from authorized infrastructure; that is the 10-lookup ceiling, not a spoofer.
If you are rehearsing this for an email-infrastructure interview, say the number out loud: 10 DNS-querying mechanisms, 2 void lookups, ptr/include/a/mx/exists/redirect count, ip4/ip6/all do not. Being able to recite the limit and then trace a real record's budget line by line is exactly the depth a reviewer is checking for.