CertaDNS

The SPF 10-lookup limit, and the void lookup limit nobody mentions

Why SPF caps DNS lookups at ten, what actually counts toward it, the separate two-void-lookup limit in the same section of RFC 7208, and what to do when you hit either.

Last reviewed

SPF evaluation is capped at ten DNS lookups. Go over and a conforming receiver returns permerror, which fails SPF — not just for whoever is spoofing you, but for the mail you actually sent. The limit exists because SPF is evaluated at SMTP time, and RFC 7208 §4.6.4 is blunt about why: without a cap, a malicious record could point a receiving server at an unbounded chain of lookups and use it as an amplifier.

The limit is on lookups performed during evaluation, not on the length of your record or the number of includes you wrote. That distinction is where most of the confusion lives.

What counts as a lookup

TermCosts a lookup?Notes
include:Yes — one, plus everything inside itRecursive. The include’s own includes count against your total, not theirs.
aYesWith or without a domain.
mxYesOne lookup, and the MX targets it resolves are capped at 10 separately.
ptrYesDeprecated by RFC 7208 §5.5. Some receivers ignore it entirely.
exists:YesThe mechanism behind macro-based dynamic SPF.
redirect=YesA modifier, not a mechanism, but it still costs one.
ip4:NoFree. This is why flattening reduces the count.
ip6:NoFree.
allNoFree.
exp=NoOnly evaluated on failure, and excluded from the limit.

The count is yours, not theirs

If you include: a provider whose record contains four more includes, those four come out of your ten. A provider that keeps its record tidy is doing you a favour you cannot see from your own record.

The other limit: void lookups

The same section of the RFC sets a second, separate limit that far fewer tools check: at most two void lookups. A void lookup is one that returns nothing — either NXDOMAIN, or an empty answer with RCODE 0.

Exceeding it is a permerror in its own right. That means a record with six total lookups, comfortably inside the famous limit, can still fail outright because three of them point at nothing. And because the record still looks short, this is genuinely difficult to spot by reading it.

In practice void lookups almost always mean the same thing: an include: for a service you stopped using. The provider decommissioned the record, your SPF still references it, and nothing complained until you added the third one.

A worked example

GitHub's record is a good one to read because it sits exactly on the line — ten of ten, with no headroom at all:

$ dig +short TXT github.com
"v=spf1 ip4:192.30.252.0/22
  include:spf.protection.outlook.com
  include:_netblocks.google.com
  include:_netblocks2.google.com
  include:mail.zendesk.com
  include:_spf.salesforce.com
  include:servers.mcsv.net
  include:mktomail.com
  include:sendgrid.net
  ip4:62.253.227.114 ip4:166.78.69.169
  ip4:166.78.69.170 ip4:166.78.71.131 ~all"
Line breaks added for readability; the record is published as a single string pair.

Eight includes, so eight lookups before anything recurses. Two of those providers have includes of their own, which brings the total to ten. The four ip4: ranges are free — someone has already done the arithmetic here and used literal ranges for the senders that allowed it.

IncludeLookups it costs
_spf.salesforce.com2
sendgrid.net2
spf.protection.outlook.com1
_netblocks.google.com1
_netblocks2.google.com1
mail.zendesk.com1
servers.mcsv.net1
mktomail.com1
Totals as of the review date. Providers change their records; this is why the count has to be re-checked rather than calculated once.

The practical reading: adding one more sending service to this domain breaks SPF for all of it. Not degrades — breaks. That is the position a lot of domains are in without knowing, because nothing warns you at nine.

You can run the same expansion against your own domain with the SPF Inspector, which shows the per-include attribution rather than just the total.

What it looks like when you go over

  • DMARC aggregate reports show SPF results of permerror rather than fail. That distinction matters — fail means “this sender is not authorised”, permerror means “your record could not be evaluated”.
  • Mail that has always been delivered starts landing in spam, usually at some receivers and not others, because not every receiver enforces the limit the same way.
  • DMARC starts depending entirely on DKIM, because the SPF half can no longer pass. If DKIM is also missing or misaligned, mail at an enforcing policy gets rejected.
  • Nothing in your own logs changes. The failure happens at the receiving end, which is why aggregate reports are the only place it surfaces.

Getting back under

  1. Remove what you no longer use. Genuinely the first step, and often the only one needed. Every void lookup is a candidate, and so is every include for a service someone set up years ago. Check your aggregate reports for whether anything is still sending through it before you pull it.
  2. Use literal ranges where the provider publishes stable ones. Some providers document a small, rarely-changing set of IP ranges. ip4: costs nothing, so moving those out of an include is free headroom — but only where the provider treats those ranges as stable, which most do not.
  3. Consolidate senders. Two marketing tools, two ticketing systems and three transactional providers is four lookups you may be able to spend elsewhere. This is an organisational fix, not a DNS one, and it is usually the most durable.
  4. Move some senders to a subdomain. The limit is per evaluated domain, so mail.example.com gets its own ten. Bulk and transactional mail on a subdomain is good practice anyway — it keeps their reputation separate from your corporate mail.
  5. Flatten, if you are going to automate it. Replacing includes with the IP ranges behind them removes the lookups entirely. It also takes a failure mode that was loud and makes it silent — see the trade-offs before you commit to it.

Do not split into a second SPF record

Publishing two v=spf1 records does not give you twenty lookups. It gives you a permerror: RFC 7208 §4.5 allows exactly one, and receivers do not pick the better one. This comes up because two providers each tell you to “add this record” — combine their mechanisms into one record instead.

Check this on your own domain

Free, no account, reads public DNS only.

Related

CertaDNS automates most of what is described above — Email Authentication. The manual route above works perfectly well; the product exists because keeping it correct as your senders change is the part that does not stay done.