Fix the SPF permerror for too many DNS lookups: what counts toward the RFC 7208 limit of 10, how nested includes add up, the two void lookup rule, lookup costs of common vendor includes, and safe fixes before you try flattening.
Home › Guides › SPF too many DNS lookups
Last updated 17 October 2026
SPF evaluation may trigger at most 10 DNS lookups. Every include, a, mx, ptr, exists and redirect costs one, including the ones inside the records you include. Go over 10 and receivers stop and return permerror, so SPF fails for every message from that domain, not just the ones from the last sender you added.
To fix it, in this order:
a, mx and ptr unless those hosts really send mail.RFC 7208, the SPF standard, says in section 4.6.4 that the terms which cause DNS queries are the include, a, mx, ptr and exists mechanisms and the redirect modifier, and that implementations must limit the total to 10 during evaluation and return permerror when the limit is exceeded. The count covers the whole evaluation, so a vendor include that itself includes three more records costs four.
Receivers word it differently. You may see spf=permerror in an Authentication-Results header, "too many DNS lookups", "SPF PermError" in a DMARC report, or "include loop / lookup limit" in a checker. They all mean the same thing.
Why it matters: DMARC only counts SPF when the result is pass and the MAIL FROM domain aligns with the From domain. With a permerror, SPF can't help DMARC at all, so a message passes DMARC only if it also carries an aligned DKIM signature. Domains at p=quarantine or p=reject without DKIM on every sender start losing mail.
| Term | Lookups | Notes |
|---|---|---|
include:vendor.example | 1 plus everything inside | Nested includes, a, mx and so on in the vendor's record all count against your 10. |
redirect=other.example | 1 plus everything inside | Same as include for counting purposes. |
a, a:host | 1 | Looks up the A/AAAA records of the domain or host. |
mx, mx:domain | 1 | Also may resolve at most 10 MX hosts' addresses per mx term, or the term itself is a permerror. |
ptr | 1 | Deprecated by RFC 7208 ("SHOULD NOT be published"). Remove it. |
exists: | 1 | Often used with macros, for example exists:%{i}._spf.example. |
ip4, ip6, all | 0 | Free. These are what flattening converts includes into. |
exp= | 0 | Only looked up later to build a rejection message; not counted. |
The TXT lookup for your own top-level record is not counted. RFC 7208 also says receivers should allow no more than two void lookups (a name that returns no records, or does not exist). A third void lookup is also a permerror, so stale includes pointing at deleted vendor records hurt twice.
A company on Microsoft 365 that has collected marketing, transactional and CRM senders over the years publishes:
v=spf1 mx a include:spf.protection.outlook.com include:_spf.google.com include:sendgrid.net include:mailgun.org include:_spf.salesforce.com -all
Looking only at the top level it seems to use 7 lookups. Expanded, it uses 13: mx 1, a 1, Microsoft 1, Google 1, SendGrid 2, Mailgun 5 and Salesforce 2. Every message from the domain now gets permerror. Removing mx and a (the MX points at Exchange Online for inbound mail, and the website host doesn't send) and the Google include left over from a migration brings it to 10, which is legal but leaves no room. Moving Mailgun to mail.example.com brings the root record down to 5.
We expanded these vendor records on 17 October 2026. The number is what one include of that value adds to your total (the include itself plus anything nested). Vendors change their records, so always recount your own.
| Include value | Service | Lookups |
|---|---|---|
spf.protection.outlook.com | Microsoft 365 / Exchange Online | 1 |
_spf.google.com | Google Workspace | 1 |
amazonses.com | Amazon SES | 1 |
servers.mcsv.net | Mailchimp | 1 |
spf.mandrillapp.com | Mailchimp Transactional (Mandrill) | 1 |
spf.brevo.com | Brevo | 1 |
mail.zendesk.com | Zendesk | 1 |
mktomail.com | Marketo | 1 |
spf.mtasv.net | Postmark | 1 |
sendgrid.net | SendGrid | 2 |
_spf.salesforce.com | Salesforce | 2 |
mailgun.org | Mailgun | 5 |
Most large vendors now list their IP ranges directly, so one include costs one lookup. The expensive ones are records that chain several includes, like Mailgun's, and anything you add twice through different vendors.
a and mx. These authorize your web server and your inbound mail servers. If your MX is Microsoft 365, Google Workspace or a filtering service, those hosts don't send your outbound mail through the mx term; the vendor include already covers it. Check your MX hosts with MX Record Lookup.ptr. Deprecated and slow. Replace it with ip4/ip6 for the hosts you meant.ip4 ranges that repeat what an include already authorizes.SPF checks the envelope sender (MAIL FROM / Return-Path), not the visible From address. Many bulk-mail and CRM platforms send with their own bounce domain, such as bounces.vendor.example. For those messages, receivers check the vendor's SPF record, not yours, so your include does nothing except use up lookups.
To check, send a test message from the service to an outside mailbox and paste the headers into the Email Header Analyzer. Look at smtp.mailfrom in Authentication-Results. If it isn't your domain (or a subdomain of it), the include is dead weight. For DMARC, set up DKIM signing with your domain at that vendor, or a custom return-path domain if the vendor offers one, which then needs its own SPF record (often a CNAME the vendor gives you). The DMARC alignment guide covers both.
SPF is evaluated per domain, so news.example.com gets its own 10-lookup budget and its own record. Send newsletters from news.example.com, transactional mail from mail.example.com and so on, and keep only the services that must send as the bare domain (usually Microsoft 365 or Google Workspace) on the root. DMARC at the organizational domain still covers subdomains through sp= or inheritance, so you don't lose protection. Microsoft also recommends subdomains for services you don't directly control.
Flattening replaces an include with the ip4/ip6 ranges it currently resolves to, which costs zero lookups. The catch is that you stop following the vendor's changes: when they add a range, your record is wrong and their mail from that range fails SPF until you notice.
spf.protection.outlook.com. Microsoft says not to, because its IP addresses change.v=spf1).spf=pass in the Email Header Analyzer, or run the whole domain through the Email Deliverability Checker.Need SPF, DMARC and MX checks across a whole client list? Run the Bulk SPF / DMARC / MX Auditor on Apify ($2 per 1,000 domains).
Count lookups with the SPF Record Checker, draft a smaller record with the SPF Record Builder, find every real sender in your DMARC reports, and check SPF, DKIM, DMARC and MX together with the Email Deliverability Checker. Platform guides: SPF for Microsoft 365, SPF, DKIM and DMARC for Google Workspace and the bulk sender requirements.
Ten. RFC 7208 limits the include, a, mx, ptr and exists mechanisms and the redirect modifier to 10 in total across the whole evaluation, including the ones inside records you include. The ip4, ip6 and all mechanisms do not count.
Yes. If you include a vendor record that itself includes three more records, that one include costs four lookups out of your 10. This is why a record with only six includes can still go over the limit.
The receiver stops evaluating and returns permerror. SPF then fails for every message from the domain, and DMARC can only pass through an aligned DKIM signature.
A DNS lookup made during SPF evaluation that returns no records or a non-existent domain. RFC 7208 says receivers should allow at most two; a third is also a permerror. Stale includes for vendors you no longer use are a common cause.
Only after removing unused senders and moving third-party senders to subdomains, and only for vendors with stable published ranges that you recheck regularly. Never flatten include:spf.protection.outlook.com, because Microsoft says its addresses change.
Runbooks, checklists and helpdesk templates: IT Admin Packs on Gumroad.