/ TL;DR
SPF allows at most 10 DNS lookups per check. Cross that limit and SPF returns a PermError — which fails authentication for every message from your domain, not only the service that tipped you over. There's also a second limit of 2 void lookups that breaks SPF the same way.
The trap is include recursion: one include counts for itself plus every lookup inside the record it points to, so a single large provider can eat four or five. Three or four services is often enough to exceed ten. The failure is silent — SPF works, you add one more service, and it breaks for weeks before anyone notices.
Fix it in order: prune includes you no longer use, replace what you can with direct IPs, then delegate streams to subdomains for a durable solution. Treat flattening as a last resort — it fixes the count but goes stale when providers change IPs.
/ 01
What the PermError is
An SPF PermError is a permanent error a receiving server returns when your SPF record can't be evaluated at all — as opposed to a plain SPF fail, which means a specific message wasn't authorized. PermError says the record itself is broken. It has a few possible causes, including malformed syntax or two SPF records on one domain, but by far the most common in practice is exceeding the limit on DNS lookups, and that's the one this guide is about.
The reason it matters so much is the blast radius. When SPF returns a PermError, the whole record is invalid, so SPF fails for every message your domain sends, well beyond the mail from the service that pushed the count over the edge. If your DMARC alignment leans on SPF, DMARC fails in cascade for that mail as well. And because every major mailbox provider factors authentication results into filtering, a domain that consistently fails SPF drifts toward the spam folder, and at some providers toward outright rejection.
The cruelty of it is how quietly it arrives. SPF worked yesterday with three sending services. You add a fourth, its include tips the total from nine to eleven, and now authentication is broken for all your mail — with no bounce at your end and no alert, just a slow slide in deliverability few teams trace to a DNS change made weeks earlier. One large 2026 scan of several million domains found that around one in twenty SPF-enabled domains already exceeds the limit, most of them unaware.
/ 02
The 10-lookup and 2-void-lookup limits
The rule is set by RFC 7208, the SPF specification. Evaluating an SPF record may trigger no more than 10 DNS lookups; a receiver that would need an eleventh must stop and return a PermError. The initial query that fetches your SPF record itself doesn't count against the ten — only the lookups caused by mechanisms inside it do.
There's a second limit that trips people far more often than they realize, because almost no guide mentions it. The same specification caps void lookups at two. A void lookup is any DNS query during evaluation that comes back empty — an NXDOMAIN because the domain no longer exists, or a NODATA because it exists but has no matching record. Exceed two of those and you get a PermError, even if your ordinary lookup count is nowhere near ten.
The void limit matters because of how records rot. The usual trigger is a stale include pointing at a provider you stopped using: they decommissioned that domain, the query now returns nothing, and two such dead includes are enough to break your SPF outright. This is a classic post-migration failure — a team moves off a vendor, leaves the old include in place, and authentication quietly breaks with a lookup count that looks perfectly healthy. When you audit, check for dead includes as carefully as you count live ones.
/ 03
What counts, and what doesn't
Not every part of an SPF record costs a lookup, and knowing the difference is how you optimize. The mechanisms that require a DNS query all count: include, a, mx, ptr, and exists, along with the redirect modifier. The ones that carry an address directly cost nothing: all, ip4, and ip6 need no DNS resolution, and neither does the exp modifier.
The number that wrecks budgets is hidden inside include, because it counts recursively. An include costs one lookup for itself, plus every lookup performed inside the record it references. A big provider's SPF record nests several sub-includes of its own, so a single line like include:_spf.google.com can consume four lookups rather than one. Stack three or four providers that each behave this way and you're at the limit without a visibly long record. Counting includes by eye is how everyone undercounts.
Two more mechanisms deserve a note. The mx mechanism triggers an MX query and then an address lookup for each server it returns, so a domain with several mail servers can spend several lookups on one mx. And ptr is both lookup-heavy and formally discouraged by the specification — if it's in your record, removing it is almost always the right call. The only way to know your real total is to walk the full include tree, which is what a proper checker does for you.
/ 04
Why the limit exists
The limit can feel arbitrary when it breaks your mail, but it's a deliberate defense. Every DNS lookup a receiver performs to evaluate SPF costs it bandwidth, time, and memory — and the receiver is doing this for every inbound message. Without a cap, an attacker could publish an SPF record engineered with dozens of nested includes, then send a flood of mail to a target's servers and force each message to trigger a cascade of DNS queries, turning SPF evaluation into an amplification vector for a denial-of-service attack.
Ten was the number the specification's authors chose to keep evaluation cheap while still allowing legitimate records room to breathe. The friction is a side effect of how email changed since SPF was written: a domain that once sent through one or two systems now routes mail through a handful of separate cloud providers, each wanting an include. The limit didn't move; the way we send did. Understanding that the cap protects receivers, rather than punishing senders, makes the right fixes obvious — the goal is to say the same thing in fewer lookups, not to fight the limit.
/ 05
How to diagnose it
Confirming the problem takes two minutes and rules out guesswork. The direct check is to run your domain through an SPF analyzer that walks the entire include tree and reports the true lookup total, the void-lookup count, and any syntax faults; eyeballing won't reveal the nested lookups that break it. Our SPF checker does exactly this: it recurses every include, counts the real total against the limit of ten, flags void lookups, and shows you which mechanisms are spending your budget.
You can corroborate it from the receiving side too. Send a message to a Gmail account, open the original, and look at the authentication results for an spf=permerror line — that's the receiver telling you directly. If you have DMARC reporting in place, a PermError shows up there as well, reported across all your senders rather than passing SPF, which is often the first hard evidence a team sees that something broke.
Either way, get a real number before you touch anything. Knowing you're at thirteen lookups with two of them void, and seeing exactly which includes are responsible, turns the fix from trial-and-error into a short, targeted edit. The domain health scanner also surfaces an SPF lookup problem alongside your other authentication signals if you want the wider picture at the same time.
/ 06
The fixes, ranked
There's a clear order of preference here, from the cleanest and most durable to the most fragile. Work down it, and stop as soon as you're safely under the limit with margin to spare.
First, prune. Audit every include, a, and mx mechanism and remove anything for a service you no longer send through. This alone resolves a surprising share of cases, because dead includes both consume lookups and cause void-lookup PermErrors. While you're in there, drop any ptr mechanism and collapse redundant includes. Second, replace with direct IPs where a provider publishes a small, stable set of addresses — swapping an include for ip4 and ip6 entries costs zero lookups, though only do this where the IPs genuinely don't change.
Third, and the best structural answer for a growing organization, is subdomain delegation. Send each stream from its own subdomain — marketing from one, transactional from another — and give each subdomain its own SPF record with its own fresh budget of ten lookups. A provider that crowded your root record now sits alone under a subdomain, nowhere near the cap, and because you're still using the provider's own include, it stays current without maintenance. For anyone sending through more than a handful of services, this is the durable fix that stops the problem recurring every time you add a tool.
Only when those options are exhausted does flattening enter the picture, and it comes with strings attached — the next section explains why it's a last resort rather than a shortcut. Whatever you change, validate the new record before you publish it, because a broken edit can fail SPF just as thoroughly as the problem you're fixing.
/ 07
Why flattening is a trap
SPF flattening replaces your lookup-consuming mechanisms with the actual IP addresses they resolve to, collapsing a record full of includes into a flat list of ip4 and ip6 entries that cost zero lookups. It does fix the PermError, immediately and completely, which is exactly why it's tempting — and why it's so often the wrong first move.
The problem is ownership of those IPs. They belong to your providers, and large providers rotate their sending ranges regularly and without notifying you. The day a provider changes its addresses, your flattened record still lists the old ones — so it now authorizes servers that no longer send your mail and fails to authorize the ones that do. Legitimate email starts failing SPF, silently, for the same reasons the original PermError did. Flattening fails to remove the maintenance burden. What it does is convert a one-time lookup problem into a standing obligation to re-flatten on a schedule, which realistically means depending on an automated service to do it for you.
It's also worth putting one myth to rest: the occasional claim that a shorter SPF record improves your sender reputation has no basis. Receivers pay no attention to brevity. What they evaluate is whether the sending server is authorized. Flattening buys you a lower lookup count and nothing else, at the cost of fragility. Reserve it for the genuine edge case where pruning and delegation fall short, and automate it if you must.
/ 08
Keeping it from coming back
Fixing the count once doesn't keep it fixed, because SPF records drift. Providers change what their own includes resolve to, teams add new tools, and your carefully-tuned nine lookups quietly become eleven again. The habit that prevents a repeat is simple: before adding any new sending service to your SPF record, check your current lookup count first, so you know whether you have room or need to delegate the newcomer to a subdomain.
A few standing rules keep records healthy. Publish exactly one SPF record per domain — two records with v=spf1 is itself a PermError. Remove a vendor's include the moment you stop using that vendor, before it becomes a void lookup. Avoid ptr entirely. And re-check the record on a schedule, monthly is reasonable, since a provider quietly restructuring its own record can push you over the limit with no change on your side at all.
None of this is difficult, but it's ongoing — the kind of maintenance that falls through the cracks until deliverability drops and someone traces it to a DNS edit from two months back. Keeping SPF lean, DMARC aligned, and every sender authenticated as your infrastructure evolves is exactly the standing operation we run — so the count never silently creeps past ten again. Start by getting a real reading with the SPF checker, and pair it with the authentication guide for the full SPF, DKIM, and DMARC picture.
/ 09 — FAQ