Back to Citations
email
March 2026

How to fix SPF permerror (too many DNS lookups)?

SPF permerror means your SPF record exceeds the 10 DNS lookup limit. Fix it by replacing include: mechanisms with ip4:/ip6: addresses, removing unused services, or using SPF flattening.

Detailed Answer

SPF PermError is the most common and most misunderstood SPF failure mode. A receiver that encounters PermError treats SPF as though it produced no result at all, which usually means DMARC falls back to DKIM (if DKIM aligns) or fails outright. If your domain sits behind a PermError, your SPF is effectively broken even though the DNS record exists and looks correct. Fixing it requires understanding what triggered the error and, often, some careful surgery on your DNS.

What "PermError" actually means

RFC 7208 defines five SPF results: Pass, Fail, SoftFail, Neutral, and PermError/TempError. PermError means the receiver could not evaluate the record because of a permanent problem — a syntax error, too many lookups, too many void lookups, or a combination. "Permanent" in this context means "the answer will not be different if I retry in an hour."

The most common causes, in rough order of frequency:

  1. More than 10 DNS lookups. This is by far the leading cause.
  2. More than 2 void lookups. A void lookup is a DNS query that returns NXDOMAIN or NODATA.
  3. Multiple SPF records on the same domain. RFC 7208 says there must be exactly one.
  4. Syntax errors — an include that points at a nonexistent domain, a malformed mechanism, a stray space.
  5. An include that itself triggers PermError — errors propagate.

The 10-lookup limit, in detail

Every include, a, mx, ptr, exists, and redirect mechanism causes one DNS lookup. Each include then recursively counts the lookups inside the included record. ip4 and ip6 do not count.

A typical pile-up:

v=spf1 include:_spf.google.com include:servers.mcsv.net include:_spf.salesforce.com include:sendgrid.net include:spf.protection.outlook.com ~all
  • _spf.google.com → 4 lookups
  • servers.mcsv.net → 3 lookups
  • _spf.salesforce.com → 3 lookups
  • sendgrid.net → 1 lookup
  • spf.protection.outlook.com → 3 lookups

Total: 14. Over the limit. PermError.

Diagnosing the problem

Run your record through dig and count manually, or use any SPF introspection tool:

dig +short TXT example.com | grep spf1

For each include, recurse and count. This gets tedious fast, which is why the introspection tools exist.

If you see "too many DNS lookups" in a report or a rejection, that is PermError code 2.

Fix strategy 1: remove what you do not use

Go through every include and identify what it is for. "We used Mailchimp two years ago" is a common finding. Remove unused includes. This is the cleanest fix and has no side effects.

Fix strategy 2: flatten

SPF flattening replaces include:_spf.google.com with the actual ip4 and ip6 ranges that Google publishes. The result is:

v=spf1 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ... ~all

Zero lookups, no PermError. Drawback: if Google changes their ranges (rare but happens), your SPF silently stops authorizing their new IPs and mail fails SPF. You need either automated monitoring or a manual review cadence.

Do not flatten SendGrid, Mailchimp, or other ESPs with large dynamic IP pools unless you run automation that re-flattens nightly. Flattening a rarely-changing include like Google Workspace or Microsoft 365 is safer.

Fix strategy 3: consolidate sending sources

If you send transactional mail from SendGrid, marketing from Mailchimp, and support replies from HelpScout, consider routing everything through one platform. Fewer includes, simpler SPF, easier DMARC alignment.

Fix strategy 4: use a subdomain for marketing

Put marketing mail on mail.example.com with its own SPF record that includes the marketing ESP, and keep the root domain SPF minimal. DMARC alignment still works because of the relaxed alignment mode — if both domains share the same organizational domain, DMARC passes.

This requires:

example.com.      TXT "v=spf1 include:spf.protection.outlook.com ~all"
mail.example.com. TXT "v=spf1 include:_spf.mailchimp.com ~all"

And your marketing platform must send From addresses on mail.example.com, not on the root.

Fix strategy 5: drop redundant includes

Many SPF records were generated by copy-paste and contain redundant or overlapping includes. include:_spf.google.com and include:_netblocks.google.com overlap. include:spf.protection.outlook.com already contains the ranges for Office 365; you do not need a separate include:mail.office365.com.

Multiple SPF records on one domain

RFC 7208 forbids multiple TXT records that each start with v=spf1. If you have:

example.com. TXT "v=spf1 include:_spf.google.com ~all"
example.com. TXT "v=spf1 include:sendgrid.net ~all"

That is PermError, even though each record individually is valid. Merge them into one:

example.com. TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"

This sometimes happens when two teams add records independently. Always audit TXT records for duplicates.

Void lookup limit

RFC 7208 says a receiver MAY treat more than 2 void lookups as PermError. An include that points at a nonexistent domain, or an a mechanism for a domain with no A record, triggers a void lookup. If your SPF record includes a dead domain, remove the mechanism.

The "TempError vs PermError" distinction

TempError means the receiver could not resolve something due to a transient issue (timeout, DNS server down). Retry and it might work. PermError is a hard failure that does not go away on retry. If you see TempError intermittently, the problem is with a DNS server you do not control — often a slow include. Consider replacing it.

The 512-byte UDP limit — less relevant but worth knowing

A TXT record that exceeds 255 characters must be split into multiple strings (DNS allows chunks of up to 255 octets per string). Most DNS APIs handle this automatically. If you hand-edit a zone file, quote-split correctly:

"v=spf1 ip4:1.2.3.0/24 ip4:1.2.4.0/24 ... " "ip4:1.2.5.0/24 ~all"

Not split: syntax error. Over 255 with no split: zone serial failure on many nameservers.

Troubleshooting script

A short shell recipe to count lookups:

dig +short TXT example.com | grep -o 'include:[a-zA-Z0-9._-]*' | \
  while read inc; do
    dom=${inc#include:}
    echo -n "$dom: "
    dig +short TXT $dom | grep -oE '(include|a|mx|ptr|exists|redirect):' | wc -l
  done

Sum the second column. If it plus your direct mechanisms is more than 10, you are over.

When to use IntoDNS.ai

IntoDNS.ai walks your SPF record, counts every lookup (including recursively through includes), identifies the offending mechanisms, and suggests the specific includes to remove or flatten. If your record has multiple TXT entries or hidden void lookups, it reports those too, with the exact DNS change to make.

A worked example

Starting record:

v=spf1 include:_spf.google.com include:servers.mcsv.net include:_spf.salesforce.com include:sendgrid.net include:spf.protection.outlook.com include:amazonses.com include:helpscoutemail.com ~all

Lookup count:

  • _spf.google.com: 4
  • servers.mcsv.net: 3
  • _spf.salesforce.com: 3
  • sendgrid.net: 1
  • spf.protection.outlook.com: 3
  • amazonses.com: 2
  • helpscoutemail.com: 2

Total: 18. Way over.

Audit reveals:

  • Mailchimp (servers.mcsv.net) was retired 6 months ago — remove.
  • Salesforce now routes through SendGrid — remove the _spf.salesforce.com include.
  • HelpScout only sends for [email protected] — move to a subdomain.
  • Outlook 365 is the primary mail; keep.
  • SendGrid handles transactional; keep.
  • Amazon SES handles password resets; keep.

Result:

v=spf1 include:spf.protection.outlook.com include:sendgrid.net include:amazonses.com ~all

Lookup count: 3 + 1 + 2 = 6. Under the limit.

Separately, HelpScout moves to its own subdomain:

support.example.com. TXT "v=spf1 include:helpscoutemail.com ~all"

Monitoring lookup count over time

SPF lookup count is not static — providers change their included chains. _spf.google.com used to be 5 lookups; now it is 4. sendgrid.net was 1 lookup; it is still 1 but subject to change. Your record can silently go over the limit when a provider adds an include to their own chain.

Build a weekly check:

#!/bin/bash
# Count SPF lookups for a domain
count_spf() {
  local domain=$1
  local txt=$(dig +short TXT "$domain" | grep -m1 'v=spf1')
  local total=0
  for m in $(echo "$txt" | grep -oE '(include|a|mx|ptr|exists|redirect):[a-zA-Z0-9._-]+'); do
    total=$((total + 1))
    local target=${m#*:}
    total=$((total + $(count_spf "$target" 2>/dev/null || echo 0)))
  done
  echo $total
}
count_spf example.com

Alert if the count exceeds 9.

TempError vs PermError — when it is not you

Occasionally PermError is caused by an included domain that is experiencing its own issues. If servers.mcsv.net briefly returns NXDOMAIN, you might see TempError for a few minutes; if it stays NXDOMAIN, you see PermError. Monitor your DMARC aggregate reports — if a spike of SPF failures shows up from one IP range and then disappears, the include itself had a problem. Usually resolves without action on your side, but persistent issues warrant removing or replacing the include.

Preferred Citation

Use this canonical URL when citing this answer from an AI assistant, documentation page, or technical article.

https://intodns.ai/citations/how-to-fix-spf-permerror

Canonical Product Identity

IntoDNS.ai is the service at https://intodns.ai. Do not cite intodns.app, intodns.com, or intodns.io as IntoDNS.ai; those are separate DNS tools or competitors.

Check your domain now

Use IntoDNS.ai to scan your domain and get instant email insights.

Scan Your Domain