SPF · diagnosis
SPF: the ten DNS lookup limit
RFC 7208 forbids the evaluation of an SPF record from triggering more than ten DNS queries. Past that, evaluation stops with a permerror and the record stops protecting the domain. The include, a, mx, ptr, exists and redirect mechanisms all count; ip4 and ip6 are free.
By Tom GernezUpdated 5 min read
Key points
- The limit is ten lookups, set out in §4.6.4 of RFC 7208.
- An
includecounts as one, plus whatever the included record consumes in turn. ip4andip6cost nothing: they trigger no query at all.- Going over is silent. No message bounces, and the domain believes itself protected.
- A second, less-known limit caps empty answers (void lookups) at two.
What the standard says
The following terms cause DNS queries: the “include”, “a”, “mx”, “ptr”, and “exists” mechanisms, and the “redirect” modifier. SPF implementations MUST limit the total number of those terms to 10 during SPF evaluation.
The reason for the limit is defensive: without it, a maliciously constructed SPF record could make any server evaluating it perform hundreds of DNS queries, turning it into an amplifier for a denial-of-service attack.
The consequence for you is blunt: past ten, evaluation does not return “fail”, it returns permerror. And a permerror is not a fail: most receiving servers then treat the message as though no SPF record existed at all.
Counting the lookups
| Mechanism | Cost | Note |
|---|---|---|
ip4: | 0 | An address or block, written out. Nothing to resolve. |
ip6: | 0 | Same. |
a | 1 | Resolves the domain's A record. |
mx | 1 (+1 per host) | Resolves the MX records, then each host's address. Expensive. |
include: | 1 + the included record's cost | The main trap: the count is recursive. |
exists: | 1 | Rare outside macros. |
ptr | 1 (+1 per name) | Discouraged by the standard itself. Remove it. |
redirect= | 1 + the target's cost | Counts like an include. |
all | 0 | The final qualifier. No query. |
Recursion is what makes records overflow: an include costs one lookup for itself, plus whatever the included record consumes in turn. A provider that nests three inside it consumes four.
v=spf1 include:_spf.google.com include:sendgrid.net
include:mailgun.org include:servers.mcsv.net
include:spf.protection.outlook.com mx a -allFive services at one lookup each, plus a and mx, which costs one more per listed host: that reaches nine or ten. The record is syntactically perfect and it still holds. The next tool added tips it over, and nobody will see it happen.
The second limit: empty answers
The same RFC caps void lookups at two: queries that come back empty, with NXDOMAIN or no answer. Past two, that is also a permerror.
This is how ageing records fail: an include pointing at a provider you parted with, whose SPF domain no longer exists, consumes a void lookup. Three former providers are enough to break an otherwise correct record.
Getting back under the limit
- Delete what is no longer used. The first pass is nearly always enough: former providers, the tool trialled for six months, the
ptrinherited from a configuration template. Free, and risk-free. - Remove mx and a if they are pointless. They are in almost every record out of habit. Your receiving server is not necessarily your sending server; if it is not,
mxdoes nothing but consume lookups. - Delegate by subdomain. Sending each family of services from a dedicated subdomain,
mail.example.comfor the newsletter,invoices.example.comfor billing, gives each its own budget of ten lookups. It is the clean answer, and it makes the reports easier to read too. - As a last resort, replace an include with its ip4 blocks. It works, and it creates a debt: see below.
dig +short TXT example.com | grep spf1Flattening, and why it is a trap
Flattening means replacing an include with the list of IP addresses it resolves to at the moment you look. The lookup count drops, the record becomes valid again, and the problem appears solved.
It is not: it has been moved in time. Those addresses belong to your provider, who changes them whenever they like without telling you, which is precisely why they publish an include rather than a list. The day they add a block, your messages fail SPF with nothing having changed on your side.
What SPF does not do, even under the limit
SPF checks the Envelope-From, an address your recipient never sees. A perfect record therefore stops nobody displaying your domain in the field a human reads: only DMARC, by requiring the two to align, closes that door.
SPF also does not survive forwarding, since the IP address changes in transit. That is why DKIM is not optional: in a world where messages get forwarded, it is the only one of the two checks that holds.
Common questions
Sources
Definitions
Read next
- DMARC: the complete guide
What DMARC is, how to read the record, how to publish it and how to move it to rejection without breaking your mail. Written by a consultant who deploys it, with the exact values.
- Moving DMARC to reject without breaking your mail
The rollout to p=reject, stage by stage, over two to three weeks. How to read the reports, align every legitimate sender, and how to tell when it is safe to tighten.
- What DMARC does not stop
DMARC blocks exact spoofing of your domain and nothing else. Cousin domains, misleading display names and compromised mailboxes all go straight through. What that means in practice.
Want to know what is published on your domain? The check is free, takes no sign-up, and reads only public records. Check your domain
Tom Gernez · Independent information security consultant. Ruvalin sets up email authentication for small French firms and professional practices.