SPF PermError and the 10 DNS Lookup Limit: Why Your SPF Record Silently Fails and How to Fix It

Nearly 5% of SPF-enabled domains exceed the 10-lookup limit and risk PermError on every message they send. Here is what counts toward the limit, how to audit your real lookup count, and how to fix it without flattening yourself into a corner.

Key Takeaways
  • RFC 7208 caps SPF evaluation at 10 DNS lookups; exceed it and receivers return PermError, which fails SPF authentication for every message from your domain.
  • The mechanisms include, a, mx, exists, redirect, and ptr each consume lookups, and nested includes multiply the count. The mechanisms ip4 and ip6 consume none.
  • A 2026 scan of 5.5 million domains found 148,655 SPF-enabled domains (about 4.8%) exceeding the limit, with include making up roughly a third of all SPF mechanisms.
  • There is also a separate limit of 2 void lookups; a single stale include pointing at a deprecated service can trigger PermError on its own.
  • Fix by auditing the expanded record, removing unused includes, dropping ptr entirely, replacing small includes with direct ip4/ip6, and treating flattening as a last resort because flattened records go stale when provider IPs change.

Your SPF record can look perfectly reasonable, six or seven includes across your normal SaaS stack, and still fail authentication on every single message. The reason is a limit most senders never think about until it bites: RFC 7208 permits only 10 DNS lookups during SPF evaluation, and every nested include counts. Cross the line and receivers return SPF PermError, which is a hard failure, not a warning.

This is not a theoretical edge case. A 2026 scan of 5.5 million domains from the Tranco Top Sites list walked every SPF include chain and found that of 3.1 million SPF-enabled domains, 148,655 (about 4.8%) exceed the 10-lookup limit and risk PermError on every message they send. This guide explains what counts toward the limit, how to audit your real expanded lookup count, and the fixes that work, in the order you should try them.

What SPF PermError Actually Means

When a receiving mail server evaluates your SPF record, it walks the record and resolves each DNS-querying mechanism it encounters. If that walk requires more than 10 DNS lookups, the receiver stops and returns a permanent error: SPF PermError, too many DNS lookups.

The consequences are severe because PermError is not a soft signal:

  • SPF authentication fails entirely for every message from the domain, not just some of them.
  • Messages can be rejected, routed to spam, or silently dropped, even when completely legitimate.
  • DMARC can fail if you were relying on SPF alignment rather than DKIM, which under a strict policy means your mail gets quarantined or rejected.
  • Your domain reputation suffers as receivers see consistently unauthenticated mail.

The limit exists for good reasons: it prevents infinite recursion loops and protects receiving servers from being used to amplify DNS load. It is a hard cap in the specification, not a suggestion, and receivers enforce it.

148,655
SPF-enabled domains found exceeding the 10-lookup limit in a 2026 scan of 5.5 million domains, roughly 4.8% of all SPF-enabled domains, each risking PermError on every message sent.

What Counts Toward the 10 Lookups

Understanding which mechanisms consume your lookup budget is the foundation of fixing the problem. The rule is simple: mechanisms that require DNS resolution count; mechanisms that state IPs directly do not.

MechanismCounts?Notes
includeYesThe biggest consumer; each include triggers a lookup, and the included record's own mechanisms count too
aYesOne lookup per a mechanism
mxYesCan consume multiple lookups, since each MX host may resolve too
existsYesOne lookup
redirectYesOne lookup, plus whatever the target record consumes
ptrYesDeprecated and explicitly discouraged by RFC 7208; remove it
ip4 / ip6NoFree; state IPs directly with no DNS resolution
allNoFree

The Nesting Trap

The reason senders blow the budget without realizing it is nesting. An include does not cost one lookup; it costs one lookup plus every DNS-querying mechanism inside the included record. Your provider's SPF record may itself contain several includes, each of which may contain more. A record that looks like eight includes on the surface can easily expand to 15 or 20 actual lookups once the chains are walked.

This is a supply chain problem. Research shows include is the most common DNS-querying mechanism by a wide margin, making up roughly a third of all SPF mechanisms in the wild. Every SaaS tool you add contributes its own include and its own nested chain, so the more services your organization adopts, the faster your lookup budget runs out, without anyone touching your record.

The other limit nobody mentions: Beyond the 10-lookup cap, RFC 7208 also allows only 2 void lookups (queries that return no result) per evaluation. This means a single stale include pointing at a decommissioned service can trigger PermError on its own, regardless of your total lookup count. One documented case involved customers who kept an include for a retired hosted email service in their record; the dead DNS entry returned empty, triggering a void lookup PermError and bouncing their mail. Removing that one stale include fixed it.

Step One: Audit Your Real Expanded Record

You cannot fix what you have not measured, and you cannot measure by eyeballing your record. The visible record tells you nothing about the nested chains. Use an SPF checker that walks the full include chain and reports your actual expanded lookup count.

What to look for in the audit:

  • Your total expanded lookup count against the limit of 10.
  • Which includes consume the most lookups, since the fix usually targets one or two heavy chains.
  • Any void lookups, which indicate a stale include pointing at something that no longer resolves.
  • Whether you have more than one SPF record on the domain, which causes PermError regardless of lookup count. This is a separate misconfiguration worth ruling out early if you are troubleshooting PermError.

You can also confirm SPF results in the wild by reviewing your DMARC aggregate reports, which show SPF pass and fail per sending source. Frequent SPF failures across all sources is a strong clue that your record is PermError-ing rather than that any individual sender is misconfigured.

The Fixes, in Order of Preference

Work through these in order. The early options are safer and more durable than flattening, which is why flattening should be the last resort rather than the first reach.

1. Remove Unused Includes

The highest-yield fix and the most commonly available one. Organizations accumulate includes for services they no longer use: the marketing tool you churned from, the help desk you replaced, the hosted security service you migrated off. Each one is burning lookups for nothing, and any that no longer resolve are also burning your void-lookup budget.

Inventory every include and confirm a live sending source behind it. If nothing sends from it, delete it. This single step resolves many PermError cases outright.

2. Remove ptr Entirely

The ptr mechanism is deprecated, explicitly discouraged by RFC 7208, and wastes lookups. The same 2026 scan found 41,728 domains still using it. If ptr is in your record, remove it. It provides nothing you cannot get more reliably another way, and it costs you budget you need.

3. Simplify or Remove mx and a

The mx mechanism is often included reflexively even when your MX hosts do not send outbound mail. If your inbound mail servers do not send on your behalf, mx is pure waste, and it can consume multiple lookups. Same for a mechanisms that are not actually sending sources. Remove what does not send, and where the sending IPs are stable and few, replace them with direct ip4 or ip6.

4. Replace Small Includes With Direct ip4/ip6

If an include exists solely to authorize a handful of stable IP addresses, replace it with ip4 or ip6 mechanisms naming those IPs directly. Because ip4 and ip6 consume zero lookups, this converts a costly include into a free mechanism. This works best for your own infrastructure and providers with small, stable, documented IP ranges.

5. Split Sending Streams Across Subdomains

This is the most elegant structural fix and the one most senders overlook. Each subdomain gets its own SPF record with its own independent 10-lookup budget. If you send marketing from one subdomain and transactional from another, each record only needs the includes for its own sources, and neither has to carry the full organizational stack.

This aligns with good sending architecture anyway, since separating streams by subdomain also isolates reputation. If your lookup budget is strained because you crammed every sending system into one record, subdomain separation solves the SPF problem and improves your reputation isolation at the same time.

Pro Tip

Make sure aligned DKIM is passing before you spend weeks fighting SPF. DMARC passes if either SPF or DKIM passes with alignment, so a domain with solid aligned DKIM has a safety margin while you fix SPF. This does not excuse a broken SPF record, since PermError still hurts your standing with receivers who check SPF independently, but it means a lookup problem is a serious bug rather than an outage. Fix SPF properly, and keep DKIM strong as your alignment backbone.

Flattening: The Last Resort

SPF flattening means resolving each DNS-querying mechanism yourself and replacing it with the resulting IP addresses, converting a complex nested record into a flat list of ip4 and ip6 entries. Because those mechanisms consume no lookups, flattening can take a record from well over 10 lookups down to one.

It works, and sometimes it is the only option when a provider's chain is unavoidably heavy. But it has a serious drawback that makes it a last resort rather than a first fix.

Why flattening is risky: A flattened record is a snapshot. The moment your provider changes its sending IPs, and providers do this routinely without notifying you, your flattened record is out of date and starts producing SPF failures for legitimate mail. You have traded a PermError for a silent drift problem that is harder to notice. If your providers use dynamic or frequently changing IPs, manual flattening will break, and it will break quietly. Anyone who flattens must monitor provider IP ranges and update the record on an ongoing basis, or use a hosted solution that automates the refresh.

There is also a record size consideration: a flattened record listing many IP ranges can grow long enough to bump into DNS record character limits, which is a formatting problem to manage on top of the staleness risk.

The sensible position: exhaust the safer fixes first (remove unused includes, drop ptr, simplify mx and a, direct ip4 for stable IPs, split by subdomain). If you still cannot get under 10 after all of that, flatten only the specific chains that are unavoidable, keep the rest as includes, and put a recurring review on the calendar to catch provider IP changes before they cause failures.

Staying Under the Limit Long Term

Getting under 10 once is easy; staying there as your organization adds tools is the real discipline. Build these habits:

  • Treat the lookup budget as a resource with an owner. Someone should approve new includes, the same way someone approves new DNS records.
  • Remove includes at churn time. When you stop using a service, remove its include in the same change, not eventually.
  • Audit quarterly with an expanded-record checker, as part of your routine authentication review. New nested lookups can appear inside a provider's record without you changing anything.
  • Use subdomain separation by default for distinct sending streams, so no single record has to carry everything.

SPF PermError is one of the most preventable authentication failures there is, and one of the most damaging because it fails silently across every message. Audit your expanded count today, clear the dead weight, and the limit stops being a threat and becomes what it should be: a constraint that keeps your record honest and fast to evaluate.

Frequently Asked Questions

RFC 7208 caps SPF evaluation at 10 DNS lookups. Mechanisms like include, a, mx, exists, redirect, and ptr each consume lookups, and nested includes multiply the count. The mechanisms ip4 and ip6 consume none. The limit prevents infinite recursion and protects receiving servers from excessive DNS load. Exceeding it returns SPF PermError, which fails authentication for every message from your domain.

SPF PermError is a permanent error returned when a receiver cannot evaluate your SPF record, most often because it exceeds the 10-lookup limit. It is a hard failure: SPF fails for every message from the domain, mail can be rejected, spam-foldered, or silently dropped, and DMARC can fail if you rely on SPF alignment. It can also be caused by syntax errors or having more than one SPF record on a domain.

Audit your real expanded record with a checker that walks include chains, then work in order: remove includes for services you no longer use, remove the deprecated ptr mechanism, drop mx and a if those hosts do not send outbound, replace small includes with direct ip4 or ip6 entries, and split sending streams across subdomains so each gets its own 10-lookup budget. Treat flattening as a last resort.

It works but carries real risk, which is why it should be a last resort. A flattened record is a snapshot of your providers' IPs at one moment. When a provider changes its sending IPs, and they do this routinely without notice, your flattened record goes stale and starts failing SPF for legitimate mail, quietly. If you flatten, you must monitor provider IP ranges and update the record continuously, or automate the refresh.

No. The ip4 and ip6 mechanisms state IP addresses directly and require no DNS resolution, so they consume zero lookups. This is why replacing a small include with direct ip4 or ip6 entries is an effective fix: it converts a costly mechanism into a free one. The tradeoff is that you must update those IPs manually if they change, so this works best for stable, documented ranges.

Share this article:
← Back to Blog