The SPF ten-lookup limit: the failure almost nobody catches
A record that reads perfectly can fail for every message you send. Here is how the limit works, why it is so easy to blow, and the four ways to get back under it.
Here is a real, valid, correct-looking SPF record:
v=spf1 include:_spf.google.com include:sendgrid.net include:servers.mcsv.net include:spf.protection.outlook.com include:mail.zendesk.com ~all
Nothing is misspelled. There is one record. It ends in a proper ~all. And it may well fail for every message the domain sends.
The rule
RFC 7208 caps the number of DNS-querying mechanisms an SPF evaluation may use at ten. The mechanisms that count are include, a, mx, ptr, exists and redirect. ip4 and ip6 cost nothing — the address is right there in the record.
The part that catches people: the limit is cumulative across everything your includes pull in behind them. Your record has five includes, so five lookups. But _spf.google.com is itself a record containing three more includes. spf.protection.outlook.com expands further. Real-world totals of fifteen or twenty from six visible includes are ordinary.
When evaluation exceeds ten, the result is permerror — a permanent error. Receivers treat that as "SPF did not pass". Not softfail, not neutral. For DMARC purposes, the SPF half of your authentication is simply gone, and if you are not DKIM-signing with an aligned domain, DMARC fails too.
Why it stays hidden
Nothing in your DNS provider's UI counts this. Most SPF "validators" only parse syntax, and the syntax is fine. Your own mail keeps arriving because you are sending to colleagues on the same platform, whose servers may not evaluate SPF at all for internal mail.
It typically shows up months later as "our invoices stopped reaching customers", and the change that caused it was adding one include to onboard a new tool.
Counting yours
The DNS checker resolves your record recursively and reports the real total, along with which include contributed what. That recursive expansion is the whole reason it exists: reading the record's syntax tells you nothing about the number that matters.
You can also do it by hand with dig, resolving each include's record and each of theirs, which is exactly as tedious as it sounds — and has to be redone every time a provider changes their record, which they do without telling anyone.
Four ways back under the limit
1. Remove what does not send. The most effective fix, every time. Most over-limit records contain includes for services the company stopped using two years ago. Every entry is an authorisation; a stale one is a standing permission you did not mean to grant.
2. Replace an include with its addresses. If a service publishes a small, stable set of IPs, put them in directly as ip4:/ip6: and drop the include. Zero lookups. The trade is real: when that provider changes IPs, your record is silently wrong, so do this only where the provider documents a stable range.
3. Delegate to a subdomain. Send bulk mail as mail.yourdomain.com with its own SPF record and its own budget of ten. Under DMARC's relaxed alignment the subdomain still aligns with your organisational domain, so DMARC keeps passing. This is the standard answer for a company with many senders and the one most worth doing properly.
4. SPF flattening, carefully. Tools exist that resolve every include and emit one flat list of IPs. It works, and it is a maintenance liability: providers change their sending ranges, and a flattened record goes stale silently — the failure mode is legitimate mail failing SPF with a record that looks impeccable. If you flatten, automate the refresh and monitor it. A flattened record maintained by hand is a scheduled outage.
While you are in there
Two other things worth fixing in the same sitting:
ptris deprecated. RFC 7208 says do not use it. It is slow, it is unreliable, and some receivers ignore it entirely.-allversus~all.-all(hard fail) is the stronger statement and the right destination. Move to it once DMARC reports show every legitimate source passing — the same discipline as a DMARC rollout, for the same reason.
Then verify it end to end
A correct record is necessary and not sufficient. SPF is evaluated against the IP that actually connects, so the only way to know it passes is to send a message and look at the result. Send one to a test address and read what came back — that is the difference between "the record is right" and "my mail passes".