SPF PermError: too many DNS lookups, and how to fix it
By Ghulam Rasool · Updated
Short answer: SPF allows at most 10 DNS lookups while a receiver evaluates your record, counting every nested include. At 11, receivers stop and return permerror, which means SPF fails for every message you send, not just the ones from the last service you added. Count your lookups, remove what you do not need, and replace lookups with IP ranges or DKIM where you can.
What the error looks like
You will usually see it in a bounce, in a DMARC aggregate report, or in the headers of a delivered message:
Authentication-Results: spf=permerror (too many DNS lookups) smtp.mailfrom=example.comMicrosoft and Google both use wording close to this. In a DMARC report the SPF result for your own servers shows as permerror even though their IP addresses are in the record.
Why the limit exists
Every SPF check makes the receiving server do DNS work on your behalf. Without a cap, a sender could publish a record that makes every receiver run hundreds of lookups, which is a cheap way to attack DNS. RFC 7208 section 4.6.4 sets the limit at 10 and requires a permerror when it is exceeded.
What counts as a lookup
| Term | Costs a lookup? | Notes |
|---|---|---|
include: | Yes, one | Plus every lookup inside the included record, recursively. |
a, a:host | Yes, one | Looks up the A and AAAA records. |
mx, mx:host | Yes, one | The MX hosts it returns are then resolved too, up to 10 of them. |
exists: | Yes, one | Mostly used with macros. |
ptr | Yes, one | Slow and discouraged by the RFC. Remove it. |
redirect= | Yes, one | Plus the lookups in the target record. |
ip4:, ip6:, all | No | Free. These are how you save lookups. |
There is a second limit people miss: void lookups. A lookup that returns nothing (the name does not exist, or has no record of that type) is a void lookup, and receivers may fail SPF after two of them. An include: for a service you cancelled years ago often turns into exactly that.
Count your lookups
You cannot count by reading your own record, because most of the cost is inside your providers' records and they change them without notice. Run the SPF checker. It follows every include, shows the count at each level, and flags void lookups. Write the number down, then use the fixes below in order: each one is safer than the next.
Five ways to get back under 10
1. Remove includes for services you no longer use
This is the fix most of the time. Old newsletter tools, a previous helpdesk, a website host you left: each still costs lookups. Check your DMARC aggregate reports or ask around before deleting, but anything that has not sent mail in your reports for a month can usually go.
2. Drop includes that do not help DMARC anyway
Many sending services (marketing platforms, CRMs, ticketing systems) send with their own Return-Path domain, such as bounces.vendor.net. SPF is checked against that domain, not yours, so your include: for them is never used and never aligns for DMARC. If the service signs with DKIM using your domain, DMARC passes through DKIM and the include can go. Check the service's documentation or a message header: if smtp.mailfrom is not your domain, your SPF record is not involved.
3. Replace a and mx with IP ranges
If a or mx point at servers that do not actually send mail (your website host, your inbound filter), remove them. If they do send, list their addresses with ip4: or ip6:. That costs nothing, but you must remember to update it if the addresses change.
4. Move a sender to a subdomain
Give a high-volume service its own subdomain, for example news.example.com, with its own SPF record. Messages from that service use the subdomain as the envelope sender, so its lookups no longer count against your main record. With DMARC's default relaxed alignment, news.example.com still aligns with example.com in the From address.
news.example.com. IN TXT "v=spf1 include:servers.mcsv.net -all"5. Flatten, carefully
Flattening replaces includes with the IP ranges they resolve to. It works, but providers change their ranges, and a stale flattened record silently fails mail from new addresses. Only flatten with a tool that re-checks and updates the record automatically, and keep includes for providers known to change often.
Things that do not work
permerror. Putting the overflow in a second record and using redirect= still counts every lookup. And +all "fixes" the error by authorising every server on the internet.Check again
After you publish the change, wait for the old record's TTL to expire and run the domain check again. It queries Google and Cloudflare separately, so you can also see whether both have picked up the new record. Aim to stay at 8 or below to leave room for your providers to grow.
Guidance on this site is checked against the RFCs and live DNS. About the author.