The protocol fact to establish first
RFC 7208 limits SPF evaluation to ten terms that cause DNS queries; include, a, mx, ptr, exists and redirect count, while ip4, ip6 and all do not consume that lookup budget.
The practical trap is that the SPF budget is consumed by evaluation, not by how short the visible TXT record looks. A root policy with only three include statements can already be close to the limit when those providers include other providers or use redirect, mx, a, or exists mechanisms internally. Count the DNS-querying terms along the path a receiver would actually evaluate for the sending IP. Keep ip4, ip6, and all separate because they do not trigger the same DNS lookup budget. This is why copying one more include from a setup guide can turn a previously passing domain into PermError even though the root SPF string still appears tidy. A useful audit saves the expanded path for each sender so later provider changes can be compared against a known baseline.
Why the obvious reading is misleading
The common mistake is counting only the include tokens in the top-level TXT record. An include can pull another policy that performs several additional DNS lookups, so the real path can be much deeper than the visible record suggests.
Do not “solve” the lookup limit by deleting whichever include looks least familiar. First map each include to a real mail stream: Google Workspace, Microsoft 365, a help-desk relay, a transactional provider, or an old system that no longer sends. Removing a live sender may make a checker green while causing that sender to fail SPF. The better cleanup is ownership-driven. For each mechanism, identify who sends through it, whether that stream still exists, and whether DKIM gives DMARC alignment independently. Stale includes are common after migrations because DNS survives longer than the application that once required it. A small sender should therefore treat SPF reduction as an inventory exercise, not a text-minification exercise.
A reproducible test from raw evidence
Start from the exact Return-Path or MAIL FROM domain in a failed message, expand every include or redirect that is reached for that sending IP, and annotate the running lookup count branch by branch.
Test from the outside with a recursive resolver and follow each include or redirect exactly as published. Record which branches return TXT, A, or MX data and which branch pushes the cumulative count over the evaluator’s budget. Then send a controlled message through every legitimate stream and inspect Authentication-Results rather than assuming one successful path proves the others. If the domain has several providers, a test message from only the primary mailbox tells you nothing about the CRM relay or forwarding appliance. This two-part check—static DNS expansion plus real messages—catches both logical SPF errors and documentation drift between the DNS record and the systems that still send.
Repair only the failing layer
Remove stale authorizations first, then simplify architecture if unrelated mail streams genuinely need separate envelope subdomains. Flatten only when somebody owns the job of tracking provider IP changes.
The repair should make the policy easier to own six months later. Remove abandoned mechanisms, consolidate duplicate entries, and prefer provider-supported include targets over hand-maintained copies of changing infrastructure. If the legitimate sender set cannot fit comfortably, review architecture instead of stacking more indirection: separate unrelated streams onto subdomains where appropriate, or lean on aligned DKIM for DMARC while keeping SPF accurate for the envelope sender. After editing, wait for DNS propagation and re-run the exact expansion that failed. Keep the before/after mechanism count in the change log so a future provider addition does not unknowingly recreate the same limit problem.
Small-sender example
A root policy with Workspace, a form service, and an outbound provider can cross the limit even though it looks tidy. If the Workspace include expands through several names, the new provider may be the final query that exposes an already-fragile tree.
A useful maintenance practice is to keep a sender inventory next to the SPF tree. For each legitimate stream, write the envelope domain, provider include target, whether the stream also signs aligned DKIM, and the DNS-querying terms introduced by that branch. This turns future changes into a capacity decision: adding a provider is not “paste this include,” it is “does the updated evaluation path still fit and does the provider belong on this domain?” If one stream is temporary, give its removal an owner and date. When a migration finishes, delete the old mechanism promptly. This kind of inventory also helps incident response because a sudden PermError can be compared with the last known tree instead of rediscovering every vendor from scratch. Avoid treating the 10-term ceiling as a target to fill; leave headroom for provider-side changes and for the fact that nested includes can evolve without a commit in your own DNS zone.
What to retain in the incident log
Keep a provider inventory beside the expanded SPF tree: envelope domain, include owner, current purpose, worst-case lookup count, and the last date a real message proved that route still sends.
The boundary that prevents over-correction
Do not “fix” a PermError by adding another include or by deleting an unknown mechanism during an incident. First prove which sender owns each branch and then send fresh test mail after the DNS change.
Field checklist
- Capture the raw evidence for SPF 10 DNS lookup limit nested includes PermError before editing DNS, queue state, or mailbox metadata.
- Confirm the cited mechanism using spf-rfc, gmail-smtp-errors rather than a generic deliverability score.
- Keep a known-good control case so the failing layer can be compared without changing several variables at once.
- Apply the narrow repair described for SPF’s 10-DNS-lookup limit: count nested includes before the 11th query becomes PermError and preserve a rollback or retry path.
- Repeat the original failing test after the change; do not substitute a different checker as proof of recovery.
- Store timestamp, affected identity, raw result, action taken, and the post-change result in the operator log.
Primary sources
Standards and provider policies can change. These links are the reference points used for this field note.
- RFC 7208 — Sender Policy FrameworkIETF / RFC Editor — SPF protocol and evaluation semantics.
- Gmail SMTP errors and codesGoogle Gmail Help — Temporary and permanent Gmail SMTP errors, including authentication and unusual-rate failures.
- Set up SPF to identify valid email sources for your Microsoft 365 domainMicrosoft Learn — Microsoft guidance on SPF includes, lookup limits, and the maintenance risk of flattening dynamic cloud IP ranges.