The 10-Lookup Limit
The 10-lookup limit
RFC 7208 caps the total number of DNS lookups a receiver must perform to evaluate an SPF record at ten. Once a receiver crosses that number, it is allowed — and most do — to return a permerror and stop evaluating. A permerror means SPF is effectively broken for that domain. DMARC alignment for SPF fails. Receivers fall back on DKIM, if any.
This is the most quietly painful SPF failure mode. The record looks reasonable. The syntax is clean. The all posture is -all. Cathedra still warns, because the record will fail at the receiver under load.
What counts as a lookup
Six mechanisms cause a DNS lookup when an SPF record is evaluated:
includeamxptrexistsredirect
Each occurrence is one lookup. Lookups inside included records also count against the parent’s total. That cascading behavior is where most domains go over the cap.
ip4 and ip6 do not count. They are direct address comparisons, no DNS involved. The closing all does not count either.
How Cathedra counts
SpfRecordCheck.php runs this regex against the record text:
preg_match_all('/\b(include|a|mx|ptr|exists|redirect)[:=]/', $spf);
This counts the lookup-causing mechanisms in the parent record only — Cathedra does not recursively resolve every include to find the real total. The check is a fast static approximation. If the count Cathedra sees exceeds nine, the warning fires.
Cathedra status: warning
Severity: medium
Finding text (verbatim, with N substituted):
SPF record contains ~N DNS-lookup mechanisms. RFC 7208 allows a maximum of 10 total lookups.
Recommendation (verbatim):
Flatten your SPF record or use an SPF flattening service to stay under the limit.
The leading ~ in “~N” is literal — Cathedra prints “approximately.” That word matters: the count is the parent’s mechanisms only. The true receiver-side total is usually higher.
A worked example
A real-world record from a mid-size client running everything:
v=spf1 include:spf.protection.outlook.com include:_spf.salesforce.com include:servers.mcsv.net include:mailgun.org include:spf.mandrillapp.com include:spf-vrf.0001.outboundmail.sendgrid.net include:_spf.intuit.com include:netfilter.zoom.us include:_spf.atlassian.net include:_spf.constantcontact.com include:_spf-bd5h.constantcontact.com -all
Cathedra counts the parent mechanisms: 11 include statements. The lookup-limit branch fires immediately:
SPF record contains ~11 DNS-lookup mechanisms. RFC 7208 allows a maximum of 10 total lookups.
The real receiver-side total is higher still. spf.protection.outlook.com typically expands to three more lookups. sendgrid.net expands to two. _spf.atlassian.net is famously nested. A receiver evaluating this record will give up at the tenth lookup and return permerror. The record reads like a valid policy but is silently broken on the receiving side.
How the count happens at the receiver
To make this concrete, here is what a strict receiver does when it sees the record above:
- Fetch the SPF record for
isidoregroup.com. (Not counted — this is the initial query.) - Encounter
include:spf.protection.outlook.com. Fetch that record. Lookup 1. - That record contains
include:spf-a.outlook.com. Fetch it. Lookup 2. - That contains
ip4lists and no further includes. Done with this branch. - Back to the parent. Encounter
include:_spf.salesforce.com. Fetch it. Lookup 3. - That contains two more includes. Fetch each. Lookups 4 and 5.
- Encounter
include:servers.mcsv.net. Lookup 6. No further includes. - Encounter
include:mailgun.org. Lookup 7. That includes one more. Lookup 8. - Encounter
include:spf.mandrillapp.com. Lookup 9. - Encounter
include:spf-vrf.0001.outboundmail.sendgrid.net. Lookup 10.
Receiver has reached ten. Per RFC, it stops and returns permerror. The remaining six include statements in the parent are never evaluated. The legitimate senders behind those six mechanisms are now treated as unauthorized — even though they are listed in the record.
The visible symptom for the client: sporadic delivery failures from whichever services happened to fall past lookup ten that week. Receivers cache their evaluations differently, so the failures look random.
How to flatten
Flattening means replacing high-cost mechanisms with the IPs they ultimately authorize. The result is more ip4 and ip6 entries (zero cost) and fewer include entries (one cost each).
Three flattening strategies, in order of preference:
1. Drop senders that aren’t actually used
Half the time, the over-limit record contains an include for a tool the client stopped using two years ago. Open the SPF record with the operations contact. Walk through each include and ask: “Are we actively sending through this service today?” Anything that gets a “we used to, but…” comes out of the record.
This is the most reliable fix and the only one that doesn’t introduce new operational debt.
2. Consolidate identical providers
A client running Mandrill (include:spf.mandrillapp.com) and Mailgun (include:mailgun.org) is paying lookup cost for two transactional senders that do the same job. Pick one, retire the other, drop one include.
3. Use a flattening service
Several vendors (EasyDMARC, Valimail, dmarcian, Scrub) provide a managed SPF record that does the flattening for you. You replace your SPF record with a single include: to their hosted record. They keep the underlying IPs up to date as your senders’ IPs change.
Operational consequences: you now depend on a third-party SPF host for mail authentication. If their record breaks, your mail authentication breaks. Use this only when the client genuinely needs more than ten senders and dropping unused includes won’t bring the count down.
Do not roll your own flattening by hand-coding IPs into the record. Sender IPs change. A hand-flattened record will silently start rejecting legitimate mail within weeks.
After flattening — verify
Re-run Cathedra. The lookup-limit warning should clear. Do not stop there — also verify enforcement posture, lookup count from a couple of public tools (dmarcian’s SPF surveyor is a useful second opinion), and that the From: domain still sends mail successfully from each service the client uses.
What you should remember
- Ten lookups total, across the parent record and every record it includes.
- Cathedra counts only the parent. Real receiver-side totals are usually higher.
- The warning text says “approximately” for a reason. Treat any warning as a real problem.
- Flatten by dropping unused senders first. Use a flattening service only when you must.
- A clean-looking record with
-allcan still fail at the receiver if it exceeds the lookup cap.
Next: what happens when a domain ends up with more than one SPF record at the apex.
Cathedra alignment: SpfRecordCheck.php — the SPF Lookup Limit branch
