CertaDNS
Skip to lesson

SPF Fundamentals · lesson 6 of 6

The limits that break SPF

After this lesson you can

Count a record against both RFC 7208 limits and say which one it will hit first.

Assumes you have read include, redirect, and nested records.

SPF is recursive, and recursion over DNS at the internet’s scale needs a bound or a single record could make a receiver perform unlimited work. So the specification imposes two limits. Nearly everyone knows about the first. The second is in the same section and catches people who have carefully counted to ten.

The ten-lookup limit

RFC 7208 §4.6.4 permits at most ten mechanisms that require a DNS lookup during a single evaluation. Exceeding it is a permerror — a hard failure. SPF cannot pass for any host, including the hosts explicitly listed.

Counts toward the limitDoes not count
include — one each, plus whatever the included record costsip4 and ip6 — no lookup at all
a — one eachall
mx — one eachThe initial lookup of the record itself
ptr and exists — one each
redirect — one

The count is cumulative across the whole tree, not per record. Ten includes at the top level is ten before any of them has been opened.

The cost is not visible in your own record

An include costs one lookup plus the entire cost of the record it points at, and that record belongs to someone else. A platform that restructures its SPF and adds two includes has just spent two of your ten, without telling you, and your record was not edited. Records go over the limit on their own.

The void-lookup limit

In the same section: more than two DNS queries during evaluation that return NXDOMAIN or an empty answer is also a permerror.

This is a separate budget. A record with six lookups, three of which point at names that no longer exist, is under the ten-lookup limit and still permanently broken. Anyone diagnosing that failure by counting to ten will find six, conclude the record is fine, and look somewhere else.

Void lookups accumulate the way you would expect: a retired vendor’s include target that has been deleted, an a: pointing at a hostname from a previous mail platform, an mx on a domain that no longer has MX records.

Counting a record by hand

Take GitHub’s record, which is a good example because it sits close to the limit:

$ dig +short TXT github.com | grep spf1
"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.2" "27.114 ip4:166.78.69.169 ip4:166.78.69.170 ip4:166.78.71.131 ~all"
Checked 2026-09-12. Eight includes at the top level; the ip4 terms are free.
  • Eight include terms: eight lookups.
  • Then each included record must be opened and its own lookups added. That is where the remaining budget goes, and where it is easy to lose track by hand.

The manual method is: fetch the record, list every lookup-costing term, then fetch each included record and repeat, keeping a running total. It is tedious and entirely doable, and doing it once by hand is worth more than reading about it — mainly because it makes obvious how much of your budget is spent inside other people’s records.

What to do when you hit it

  • Remove senders you no longer use. Always first. Most over-limit records contain at least one vendor nobody has used in two years, and those are also the likeliest source of void lookups.
  • Move senders to a subdomain. A platform sending as mail.example.com needs an include in that domain’s record, not the parent’s. This is the cleanest structural fix and it also improves your DMARC reporting later.
  • Replace an include with the addresses behind it — flattening. It works, and it converts a loud failure into a silent one: when the platform changes its addresses, your record is wrong and nothing tells you. Only do this with automation that re-checks.
  • Use exists with a dynamic backend, if your DNS provider supports it. One lookup regardless of how many senders you have.
Knowledge check

A domain’s SPF record contains six include terms. Two of the included domains were decommissioned last year and no longer resolve. Receivers report permerror. What is the most likely cause?

Try it on a real domain

Free, no account, public DNS only.

Go deeper

Last reviewed