PostureAudit

SPF “too many DNS lookups”: what causes permerror, and how to fix it

An SPF record is allowed ten DNS lookups. That count includes every lookup performed inside the records you reference with include:, and inside the records those reference. Go over the limit and receiving mail servers return permerror and treat your domain as though it has no SPF record at all — while the record itself still looks completely valid.

Why this failure is so easy to miss

Most SPF validators check the syntax of the record in front of them. The lookup limit isn't a syntax problem — the record is perfectly well-formed. The cost is hidden inside the providers you reference.

Adding one SaaS vendor to your SPF looks like adding one term. But include:_spf.google.com is not one lookup: Google's record itself contains further include: terms, each of which costs another. A single vendor can consume three or four of your ten.

The failure is silent. Nothing bounces with a message saying "SPF limit exceeded." Mail simply starts failing authentication, which usually shows up as reduced inbox placement or DMARC failures — symptoms that point you at everything except the actual cause.

Which terms count toward the limit

RFC 7208 §4.6.4 counts the terms that require a DNS query. Address mechanisms are free, because they need no lookup:

Costs a lookupFree
include:, a, mx, ptr, exists:, redirect= ip4:, ip6:, all

Two further rules catch people out. The limit is on lookups performed, not terms written — so a nested chain three levels deep still spends from the same budget of ten. And more than two void lookups (queries returning no record) is itself a permerror, independent of the count.

A record that fails

v=spf1 include:_spf.google.com include:spf.protection.outlook.com
       include:sendgrid.net include:mail.zendesk.com
       include:_spf.salesforce.com include:servers.mcsv.net -all

Six include: terms — but each expands. Google alone typically adds three or four. This record resolves to well over ten and returns permerror, so every message it was meant to authorise fails SPF.

How to fix it

1. Remove senders you no longer use

The cheapest fix, and usually available. SPF records accumulate vendors long after the vendor is gone. Every removed include: frees its entire nested cost, not just one lookup.

2. Flatten stable includes to IP ranges

Replace an include: with the ip4: and ip6: ranges it resolves to. Those cost nothing. The trade-off is real and worth stating plainly: if the provider changes their sending IPs, your SPF silently stops authorising them. Only flatten providers who publish stable ranges, and re-check periodically.

3. Use subdomains for bulk senders

Send marketing mail from news.yourdomain.com with its own SPF record and its own budget of ten. This is the structurally correct fix rather than a workaround — it also isolates your transactional mail's reputation from your marketing reputation.

4. Drop ptr entirely

RFC 7208 deprecates it, some receivers ignore it, and it costs a lookup.

Check after every change. The limit is exceeded by other people's records as much as your own — a provider restructuring their SPF can push you over without you touching anything. This is worth re-checking on a schedule, not once.

Related failures worth checking at the same time

The checker above reports all of these, along with DKIM, MTA-STS, TLS-RPT and DNSSEC. There is also an API if you need to check domains programmatically.