Deliverability & Infra

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.com costs 1 lookup to fetch that record, plus every DNS-querying mechanism inside it, recursively. A single include for a large provider routinely pulls in 3 to 6 nested lookups.
  • a and mx cost 1 lookup each. For mx, 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.
  • ptr costs 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."
  • exists costs 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 -> 0
  • include:_spf.google.com -> 1, and that record contains three nested include mechanisms -> +3 = 4
  • include:sendgrid.net -> 1 -> 5
  • include:servers.mcsv.net -> 1, with its own nested includes -> often +2 = 8
  • a: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.

your turn

Stop reading about interviews. Start training for yours.