What the error means
SPF lets a domain publish which servers may send its email. To check a message, the receiving server reads your SPF record and follows it. Some terms in the record make the receiver query DNS again: include, a, mx, ptr, exists and the redirect modifier. RFC 7208, the SPF standard, limits those terms to 10 per evaluation, counting everything inside included records too.
Go over the limit and the result is permerror, a permanent error. Receivers treat that as SPF failing. The terms ip4, ip6 and all need no DNS query, so they never count.
There is a second, less known limit: no more than 2 void lookups, meaning queries that return nothing (the name doesn't exist, or has no matching record). An include pointing to a service you cancelled years ago is a typical cause. Above 2, SPF also fails with permerror.
Why it matters more than it looks
A broken SPF record doesn't bounce mail by itself. The damage shows up through DMARC. DMARC passes only if SPF or DKIM passes and matches the domain in the From address. With SPF in permerror, every message depends on DKIM alone. Any sender without working DKIM, such as a scanner, a CRM or a forgotten web form, starts failing DMARC and lands in spam or gets rejected, depending on your policy.
How lookups are counted: a worked example
Take this record for example.com. The provider names use the reserved .example domain, so the counts are illustrative; real providers change their records over time.
v=spf1 include:_spf.mailhost.example include:mail.crm.example include:bounce.esp.example a mx ~all
| Term | Lookups | Why |
|---|---|---|
include:_spf.mailhost.example | 3 | 1 for the include, plus 2 more includes inside that record |
include:mail.crm.example | 2 | 1 for the include, plus 1 nested include |
include:bounce.esp.example | 4 | 1 for the include, plus 3 nested includes |
a | 1 | Looks up the domain's own address records |
mx | 1 | Looks up the domain's mail servers |
~all | 0 | No DNS query |
| Total | 11 | Over the limit: SPF returns permerror |
The record looks short, five terms, yet it needs 11 lookups because of what sits inside the includes. That is why the problem usually appears suddenly: one of your providers adds a nested include to its record, and your total tips over 10 without anything changing on your side.
How to check your own record
The quickest way is the Email Security Checker: enter your domain and it builds the full include tree, shows the cost of every branch, and counts void lookups separately. It also flags includes that point to nothing and includes that end in +all.
To check by hand, read your record and then each include in turn:
nslookup -type=TXT example.com
nslookup -type=TXT _spf.mailhost.example
or with dig:
dig +short TXT example.com
dig +short TXT _spf.mailhost.example
Count one for every include, a, mx, ptr, exists and redirect, at every level. Hand-counting works for small records but is easy to get wrong once includes nest three deep.
Five ways to fix it, safest first
1. Remove includes for services you no longer use
Old newsletter platforms, a previous email provider, a trial helpdesk: SPF records collect leftovers. Each one costs at least one lookup, and if the service has since removed its record, it is also a void lookup. Ask each team what actually sends mail as your domain today, and delete the rest.
2. Remove includes that SPF never uses
SPF checks the envelope sender, the address in the Return-Path header, not the From address people see. Many email platforms send with their own bounce domain, so your SPF record isn't consulted for their mail at all, and their include adds cost without benefit. Check a message the service sent: paste its headers into the header analyzer and look at the Return-Path domain. If it isn't your domain, that include is probably unnecessary, and DMARC will rely on the service's DKIM signature instead. Confirm with the provider's documentation before removing it.
3. Replace a and mx with fixed addresses
If your own mail servers have stable IP addresses, list them with ip4: or ip6:. Each a or mx you replace saves one lookup. Only do this for addresses you control and that rarely change.
4. Move some senders to a subdomain
Each domain name has its own SPF record and its own 10-lookup budget. Sending marketing mail from news.example.com or support mail from help.example.com lets each subdomain carry only the includes it needs. Set up DKIM for each subdomain too, so DMARC passes through DKIM as well.
5. Flatten, as a last resort
Flattening replaces includes with the IP ranges they currently contain, so the record costs no lookups at all. The catch: providers change their ranges, and a flattened record doesn't follow. When it falls out of date, legitimate mail starts failing SPF. If you flatten, flatten only the smallest includes, keep the large providers as includes, and re-check the result every month. The Email Security Checker produces a flattened version when you are over the limit, to use as a starting point.
What not to do
- Don't publish a second SPF record to fit more includes. Two SPF records on one name is itself a
permerror. Merge everything into one. - Don't switch to
+allor?allto make the error go away. That lets anyone send as your domain. - Don't change
-allto~allexpecting it to help. The qualifier onallhas nothing to do with the lookup limit. - Don't use
ptr. It is deprecated, slow, and costs lookups. - Don't delete SPF entirely. No record is worse than a long one: receivers get no sender policy at all.
Make SPF failures survivable with DKIM
The lasting fix is to make sure every service that sends as your domain also signs with DKIM using your domain. Then a future SPF problem, such as a provider adding one more nested include, no longer breaks DMARC, because aligned DKIM passes on its own. Under the 2026 DMARC standard (RFC 9989) this is still how DMARC works: either aligned SPF or aligned DKIM is enough.
Frequently asked questions
Do ip4 and ip6 count toward the 10-lookup limit?
No. Only include, a, mx, ptr, exists and redirect cause DNS queries and count. ip4, ip6 and all are free.
Do includes inside included records count?
Yes. The limit applies to the whole evaluation, so every term inside every nested include adds to the total.
Will changing -all to ~all fix a permerror?
No. The all qualifier only decides what happens to unlisted senders. A permerror comes from the number of lookups, which ~all doesn't change.
How long does a fix take to work?
Receivers see the new record once the old one expires from their cache, usually within the record's TTL. Check the TTL with the DNS Lookup tool.
Can DMARC still pass while SPF has a permerror?
Yes, if the message has a valid DKIM signature aligned with the From domain. Messages without aligned DKIM fail DMARC.