SPF: too many DNS lookups
SPF permits a maximum of 10 DNS lookups while evaluating a record, counting every include, a, mx, exists and redirect, including nested ones. Cross the limit and evaluation stops with a permanent error, which receivers treat as having no SPF record at all — not as a partial pass.
Written by the SieveGuard team from the checks our own free tools run. Last reviewed .
Why this breaks setups that nobody touched
The count is not of the mechanisms in your record. It is of every lookup performed while resolving it, including the ones inside your providers’ records. Your record can be unchanged for two years and still cross the line the day a provider adds an include: to theirs.
When it happens the result is total, not partial: evaluation halts at the eleventh lookup and returns permerror. Under DMARC a permerror is not a pass, so a domain relying on SPF for alignment loses it, everywhere, at once. That is the signature of this failure — mail that was fine yesterday failing from every sender simultaneously with no change on your side.
What counts and what does not
| Mechanism | Costs a lookup? | Note |
|---|---|---|
| include: | Yes — 1, plus everything inside it | The compounding one |
| a | Yes — 1 | Plus one more per additional name |
| mx | Yes — 1, plus one per MX host returned | A 5-host MX costs 6, not 1 |
| exists: | Yes — 1 | Rare in hand-written records |
| redirect= | Yes — 1, plus the target’s own lookups | Counts even though it is a modifier |
| ip4: / ip6: | No — free | The basis of every fix below |
| all | No | Terminates evaluation |
The mx row is the one that catches people. It is a single token in your record and it can cost six lookups on a domain with five mail exchangers. There is a separate, less-known cap of 10 on ptr/mx host expansions inside a single mechanism, but you will hit the main limit first.
What each provider costs you
Counts at the time of writing, resolved from each provider’s published record. Treat them as indicative rather than fixed — the number changing under you is the entire subject of this page. Re-count yours with the command below rather than trusting any table, including this one.
| Provider | Include | Approx. lookups |
|---|---|---|
| Google Workspace | _spf.google.com | 4 |
| Microsoft 365 | spf.protection.outlook.com | 3 |
| SendGrid | sendgrid.net | 3 |
| Mailchimp | servers.mcsv.net | 2 |
| Amazon SES (regional) | amazonses.com | 2 |
| Zoho Mail | zoho.com | 3 |
| Brevo | spf.brevo.com | 2 |
| Salesforce | _spf.salesforce.com | 4 |
| HubSpot | _spf.hubspot.com | 2 |
| Zendesk | mail.zendesk.com | 2 |
Add them up and the arithmetic is unforgiving. Google plus Salesforce plus SendGrid is already eleven before you have listed a single server of your own — and each include: token costs one on top of what it resolves to.
Counting yours
The recursive count is what matters, so expanding one level by hand is not enough. Walk the tree:
dig +short TXT yourdomain.com | grep spf1 # then, for every include: you find, repeat on that name dig +short TXT _spf.google.com dig +short TXT sendgrid.net
Keep going until nothing new appears, counting one for every include, a, mx, exists and redirect encountered anywhere in the tree. Or skip the arithmetic — our SPF checker resolves the chain and reports the count.
Getting back under the limit
Remove senders you stopped using
Always first, always the biggest win, and almost always available. Records accumulate providers the way a codebase accumulates dependencies — the trial ESP from three years ago is still in there costing three lookups. Nothing else on this list is free; this one is.
Replace an include with the IPs behind it
ip4: costs nothing. If a provider publishes a stable, small range, inlining it converts several lookups into zero:
before: include:mail.example-esp.net → 2 lookups after: ip4:198.51.100.0/24 ip4:203.0.113.9 → 0 lookups
The trade is real and worth stating plainly: you have taken on the job of noticing when that provider changes their IPs, and they will not tell you. Only do this for providers who publish a stable range and document it, and put a calendar reminder against it.
Move bulk sending to a subdomain
The cleanest structural fix. Each domain gets its own budget of ten, so splitting senders across names multiplies the budget instead of rationing it:
yourdomain.com v=spf1 include:_spf.google.com ~all → 4 of 10 mail.yourdomain.com v=spf1 include:sendgrid.net ~all → 3 of 10 news.yourdomain.com v=spf1 include:servers.mcsv.net ~all → 2 of 10
It also separates reputation: a marketing campaign that generates complaints damages news.yourdomain.com and leaves your corporate mail alone. That benefit usually outlives the lookup problem that prompted the change.
SPF flattening — understand the trade first
Flattening resolves every include to literal IPs and publishes the result. It always works and it is the option most likely to hurt you later:
- The record goes stale silently. When Google rotates a range, your flattened copy still lists the old one. Mail from the new servers fails SPF and nothing announces it.
- It gets long. Flattening Google alone runs to several hundred bytes, which drags in the 255-byte string-splitting rules.
- Automated flatteners add a dependency. A third party now controls a DNS record that gates your mail, and their outage is your outage.
Reach for it when subdomain separation is genuinely not available, not as the first answer.
Sources
FAQ
What is the exact SPF lookup limit?+
Ten DNS-querying mechanisms per evaluation, defined in RFC 7208 section 4.6.4. It counts include, a, mx, exists and redirect, including every one encountered inside nested records. Exceeding it must return permerror.
What happens when I exceed it?+
Evaluation stops and returns a permanent error. Receivers treat permerror as no SPF result at all, so under DMARC you lose SPF alignment completely. It is not a partial pass and it is not a warning.
Do ip4 and ip6 mechanisms count?+
No. They resolve nothing, so they are free. That is why every fix for this problem eventually involves converting lookups into literal addresses, and why a record full of ip4 entries can be long without being at risk.
Why did my SPF suddenly break when I changed nothing?+
Because the count includes your providers’ records, not just yours. When a provider adds an include to their own record, every domain that includes them gains that lookup. Domains sitting at nine or ten cross the line the same day, without any change on their side.
Is SPF flattening safe?+
It works, but it moves a maintenance burden onto you. Flattened records go stale silently when a provider rotates IP ranges, and mail from the new servers then fails SPF with nothing to announce it. Prefer removing unused senders or splitting across subdomains first.
Does the limit apply per domain or per message?+
Per evaluation, which in practice means per domain being checked. Each domain and subdomain gets its own budget of ten — which is exactly why moving bulk senders onto a subdomain is the most durable fix available.
Check it on a real domain
Free, no signup, DNS-only. Nothing you type leaves the lookup.