You wire up Google Workspace for team mail, plug in SendGrid for password resets, add Zendesk for support, and link HubSpot for newsletters. Each onboarding checklist ends with the exact same line: Add this include to your SPF TXT record.
You paste them together, save DNS, and everything appears fine. Then customer complaints roll in: corporate clients with strict mail servers never receive their login emails. When you inspect the bounce headers, you spot the culprit:
550 5.7.23 Resent-Message rejected: SPF PermError: too many DNS-lookups
If your DMARC policy is set to p=reject or p=quarantine, an SPF PermError fails DMARC evaluation immediately. Here is why this happens, how the math works, and how to fix it before delivery tanks.
The RFC 7208 10-Lookup Limit
Receiving mail transfer agents (MTAs) evaluate your SPF record against RFC 7208. Section 4.6.4 specifies a hard limit: an SPF check must not perform more than 10 DNS queries that require resolution.
Why 10? Bad actors can weaponize SPF for DNS reflection attacks. If an attacker forges mail from a domain whose SPF references dozens of external zones, receiving servers flood those nameservers with recursive queries just to verify one spoofed message.
The 10-lookup rule prevents abuse, but it catches developers off guard because nested lookups count against your domain quota, not the vendor’s.
Which Mechanisms Count?
Not every token in an SPF string incurs a lookup:
-
Consumes 1 lookup:
include,a,mx,ptr,exists,redirect -
Zero lookups (free):
ip4,ip6,all
Consider this typical apex record:
v=spf1 include:_spf.google.com include:sendgrid.net include:mail.zendesk.com include:spf.protection.outlook.com -all
On paper, that looks like 4 lookups. In reality:
-
include:_spf.google.com(1) queries_netblocks.google.com(2),_netblocks2.google.com(3), and_netblocks3.google.com(4). -
include:spf.protection.outlook.comconsumes another 1-2 recursive lookups. -
include:mail.zendesk.comresolves its own SPF tree. -
include:sendgrid.netresolves SendGrid’s delivery cluster.
By the time the MTA follows the third include, your lookup counter hits 11. Evaluation halts immediately and outputs PermError.
If you are assembling records across multiple providers or parsing an existing line, you can use the browser-based SPF Record Generator and Parser to preview vendor presets, validate CIDR notation, and inspect your total mechanism count before committing DNS changes.
The Void DNS Lookup Trap
RFC 7208 Section 4.6.4 also enforces a Void Lookup Limit.
If an MTA queries a domain in an include: or a: mechanism and receives NXDOMAIN (non-existent domain) or NODATA (empty response), RFC 7208 caps these at at most 2.
When an old marketing or transactional provider shuts down an SPF endpoint, that single dead include consumes half your allowance. Two defunct includes trigger an instant PermError, even if your total lookup count is only 4.
Anti-Pattern: Multiple SPF Records
When records grow long, a common reflex is creating a second TXT record:
TXT @ "v=spf1 include:_spf.google.com ~all"
TXT @ "v=spf1 include:sendgrid.net -all"
Never do this. RFC 7208 Section 3.2 explicitly states a domain MUST NOT have multiple v=spf1 TXT records. If an MTA sees more than one SPF record on the same host, it immediately returns PermError. All directives must live in a single record.
How to Fix It
1. Subdomain Delegation (Recommended)
Instead of authorizing every third-party service on your root apex (company.com), delegate dedicated subdomains:
- Staff email:
company.com->v=spf1 include:_spf.google.com ~all(4 lookups) - Transactional:
mail.company.com->v=spf1 include:sendgrid.net ~all(2 lookups) - Marketing:
news.company.com->v=spf1 include:servers.mcsv.net ~all(2 lookups)
This isolates reputation, simplifies DMARC alignment, and avoids the 10-lookup ceiling.
2. SPF Flattening
If you must send from apex, replace recursive include: domains with their static IP ranges (ip4:198.51.100.0/24). Because ip4 and ip6 take zero lookups, you can list many blocks safely. However, you must monitor vendor IP changes and update your records when ranges rotate.
Summary Checklist
- Keep total query depth strictly under 10.
- Prune decommissioned vendor includes immediately to prevent void lookup penalties.
- Consolidate into a single TXT record starting with
v=spf1. - Delegate dedicated subdomains for automated email streams.
When reviewing your DNS configuration, test syntax with the Nutilz SPF Record Generator alongside dig TXT yourdomain.com to verify your authentication headers resolve cleanly.
Top comments (0)