Fix SPF “too many DNS lookups” (the 10-lookup limit)
SPF allows at most 10 DNS lookups; go over and SPF fails with a PermError. How lookups are counted, how to find costly includes, and safe ways to get under 10.
Updated September 30, 2026
SPF (RFC 7208) lets a receiver make at most 10 DNS lookups while checking your record. The mechanisms include, a, mx, ptr, exists and the redirect modifier each count, and so does every one of those inside the records you include. Go past 10 and SPF returns permerror, which DMARC treats as a fail. Fix it by removing senders you don’t use, moving senders that don’t need your SPF record out of it, and putting bulk mail on subdomains.
What the error looks like
The same problem shows up under different names depending on where you look:
- In checkers: “SPF PermError: too many DNS lookups”, “SPF record exceeds 10 lookups” or “too many included lookups”.
- In message headers:
spf=permerrorinAuthentication-Results, orReceived-SPF: permerror. - In DMARC aggregate reports: an SPF result of
permerrorfor your own servers.
It often appears “suddenly” after you add one more service, or when a provider you include adds a nested include to its own record without telling you. Receivers typically count as they evaluate, so senders listed early may still pass while those after the limit fail, which makes the problem look intermittent.
What counts toward the 10-lookup limit
| Term | Counts? | Notes |
|---|---|---|
include: | Yes, 1 | Plus every counted term inside the included record, recursively. |
a | Yes, 1 | Also a:host.example.com. |
mx | Yes, 1 | The address lookups for each MX host have their own separate cap of 10 per mx mechanism. |
exists: | Yes, 1 | Used for macro-based dynamic SPF. |
ptr | Yes, 1 | Deprecated. Remove it. |
redirect= | Yes, 1 | Plus the terms in the target record. |
ip4:, ip6: | No | No DNS query needed, so these are free. |
all | No | Matches everything. |
exp= | No | Only fetched after a fail, to build an explanation. |
| Your own TXT record | No | The first query that fetches your SPF record isn’t counted. |
The 2 void-lookup limit
RFC 7208 also says receivers should allow no more than 2 “void” lookups: queries that return no records or a nonexistent domain. A third also produces permerror. The usual cause is an include: for a service you cancelled whose SPF record has since been deleted, or a typo in an include domain. These don’t just waste a lookup; they point at a name someone else might be able to register.
Worked example: counting lookups
Here’s a record for a company with a CRM, a help desk, a newsletter tool and an old payroll service (all names are placeholders):
v=spf1 a mx include:_spf.crm.example include:spf.helpdesk.example include:mail.newsletter.example include:_spf.payroll.example ~all| Term | What it contains | Lookups |
|---|---|---|
a | Your web server’s A record | 1 |
mx | Your inbound mail hosts | 1 |
include:_spf.crm.example | Three more includes for the CRM’s IP ranges | 1 + 3 = 4 |
include:spf.helpdesk.example | One include for the help desk’s relay | 1 + 1 = 2 |
include:mail.newsletter.example | Only ip4 ranges | 1 |
include:_spf.payroll.example | An include, which itself ends with a redirect | 1 + 1 + 1 = 3 |
| Total | 12 (over the limit) |
Working through the fixes below:
- The web server doesn’t send mail, and the MX hosts only receive it: remove
aandmx(−2). - Payroll moved to a new vendor last year: remove the old include (−3).
- The newsletter tool supports a custom return-path, so newsletters go out with
news.example.comas the envelope sender and that subdomain gets its own SPF record (−1).
v=spf1 include:_spf.crm.example include:spf.helpdesk.example ~allAim for headroom, not exactly 10: at 6 or 7 you can add a sender, or absorb a provider growing its own record, without an emergency.
How to audit your SPF record
Counting by hand means fetching every included record, and every record those include. The SPF checker does it for you: it expands the whole tree, shows the lookup count for each include and flags void lookups. Then list what each include is for, and check your DMARC aggregate reports for the IPs actually sending as your domain.
Count your SPF lookups
See your SPF record, its total DNS-lookup count and any void lookups, plus DMARC and DKIM results.
How to fix “too many DNS lookups”, in order
1. Remove senders you don’t use
Old includes are the cheapest fix. Every service you stopped using, trial you never finished, and duplicate include (for example the same provider listed under two names) can go. If you aren’t sure, your DMARC reports tell you whether any mail still comes from that service’s IPs.
2. Take out senders that don’t need your SPF record
SPF only checks the envelope sender domain. Many email platforms send with their own return-path domain, so your SPF record is never consulted for their mail, and their include does nothing but cost lookups. For DMARC these senders pass on DKIM (or on a custom return-path subdomain with its own SPF). Confirm in a delivered message’s Return-Path header, or in your DMARC reports, before removing an include.
3. Put bulk senders on subdomains
Give marketing or transactional platforms their own subdomain, such as news.example.com or mail.example.com, as the envelope sender or the From domain. Each subdomain has its own SPF record with its own 10 lookups, and relaxed DMARC alignment still works. This also keeps a newsletter’s reputation separate from your staff mail.
4. Replace a and mx with ip4 or ip6 where the addresses are stable
If your own servers send mail from fixed IPs, list them as ip4: or ip6:, which cost nothing. Do this only for addresses you control and that rarely change.
5. SPF flattening, with care
Flattening replaces includes with the IP ranges they currently resolve to. It gets you to near-zero lookups, but a provider can change its ranges at any time, and a static flattened record silently stops matching when it does. If you flatten, re-check the source records automatically and often, and don’t flatten providers that say not to. Flattened records also grow long quickly.
6. Hosted or dynamic SPF
Hosted SPF services publish your record for you and manage the includes, often with an include: or redirect= from your domain. Some use SPF macros with exists: so the receiver asks about the exact sending IP in one lookup. With DMARC Dojo’s hosted SPF, your domain’s record is a redirect= to one we manage, and any sender change that would take it over 10 lookups is refused before it’s published.
More on SPF syntax is in what SPF is, and the SPF record generator builds a clean record from your list of senders.
Frequently asked questions
Can I ask receivers to allow more than 10 lookups?
No. The limit is part of RFC 7208 and there’s no tag to raise it. A receiver that happens to be lenient doesn’t help you with the ones that aren’t.
Does include count as one lookup no matter what’s inside?
No. The include itself counts as one, and every include, a, mx, ptr, exists and redirect inside the included record counts too, all the way down.
Does SPF permerror mean my mail is rejected?
Not necessarily. For DMARC it’s a fail, so mail still passes if DKIM passes and aligns. Without aligned DKIM, mail can go to spam or be rejected under p=quarantine or p=reject.
Is there a limit on the number of ip4 entries?
Not on lookups, since ip4 and ip6 are free. The practical limit is size: long records need splitting into 255-character strings, and very large DNS answers can cause problems for some resolvers.
Why did my SPF record go over 10 without me changing it?
A provider you include added nested includes to its own record. Your count depends on records other companies control, so check it periodically and keep a few lookups spare.