How-to SPF

Fixing 'SPF PermError: Too Many DNS Lookups'

RFC 7208 caps SPF evaluation at 10 DNS lookups. Hit that limit and DMARC fails on the SPF side for every recipient. Here's how the cap actually counts and the two-line fix.

May 19, 2026 · 5 min read

TL;DR

  • 1 SPF caps at 10 DNS lookups per evaluation (RFC 7208); exceeding it triggers PermError and silently fails authentication.
  • 2 Each include: counts, and includes that contain includes count recursively. Most ESPs already burn 3-5 lookups.
  • 3 Fixes: SPF flattening (inline IPs), removing unused includes, or split sending across subdomains with their own SPF.

What it does

SPF records evaluate by following <code>include:</code> chains. Each <code>include:</code>, <code>a</code>, <code>mx</code>, <code>ptr</code>, or <code>exists</code> mechanism in the record triggers a DNS query. RFC 7208 caps the total at 10 — exceed it and the receiver returns PermError, which DMARC treats as a hard fail.

The cap exists for a reason: SPF evaluation is on the inbound mail path, so a 50-include record would slow every message delivery worldwide and create amplification opportunities for DDoS via DNS queries. Ten is a deliberate ceiling, not a soft suggestion.

Most operators don't realize they've hit the limit until DMARC reports start showing PermError. The classic case: a small business adds Google Workspace (3 lookups), Microsoft 365 (3 lookups), SendGrid (2 lookups), and Mailchimp (2 lookups) and silently lands at 10. The next ESP they add takes them to PermError and every outbound message fails authentication, even though SPF still "looks fine" in DNS.

How it works

  1. 1

    Use PhishFence's <a href="/tools/spf-lookup" class="text-brand-600 hover:underline">SPF checker</a> (or any SPF-aware diagnostic) to count current lookups. The tool surfaces the chained <code>include:</code> includes and totals the cost.

  2. 2

    Remove unused <code>include:</code>s. Most domains have 2-3 historical includes for ESPs they no longer use; deleting those is a free 3-6 lookups back.

  3. 3

    Flatten heavy <code>include:</code>s into raw IPs. Tools like the <a href="/tools/spf-flattener" class="text-brand-600 hover:underline">SPF flattener</a> resolve the include chain at publish time and replace it with literal <code>ip4:</code> / <code>ip6:</code> mechanisms. Trade-off: you have to re-flatten whenever an ESP's IP set changes.

  4. 4

    Move some senders to a subdomain with its own SPF. Marketing can send from <code>mail.yourbrand.com</code> with one SPF record listing only its ESPs; the apex can stay clean. Each subdomain gets its own 10-lookup budget.

  5. 5

    Verify with a test send + DMARC report. Don't trust the static count alone — confirm receivers see a pass result by sending a test message and checking the <code>Authentication-Results</code> header at the destination mailbox.

Common pitfalls

  • <strong>Flattening without a re-flatten schedule.</strong> ESPs add and remove IPs constantly. A flattened SPF that was correct in March can be incorrect by June. Either re-flatten weekly via automation, or accept the trade-off and use <code>include:</code> chains.

  • <strong>Counting only top-level <code>include:</code>s.</strong> Nested includes count recursively. <code>include:_spf.google.com</code> chains to several sub-includes; the total can be 3-5 lookups per top-level include.

  • <strong>Adding <code>ptr</code> mechanisms.</strong> <code>ptr</code> is deprecated in RFC 7208 and triggers slow DNS reverse lookups that some receivers refuse to perform. Use <code>ip4:</code> / <code>ip6:</code> instead.

  • <strong>Splitting into multiple SPF records.</strong> Receivers MUST treat multiple SPF records on one domain as PermError. Always merge into a single record — TXT records can be split across quoted strings if needed but must be syntactically one record.