What permerror means
RFC 7208 defines seven SPF results: none, neutral, pass, fail, softfail, temperror and permerror. A permerror (permanent error) says the record is constructed in a way that makes correct evaluation impossible. Unlike a temperror, retrying does not help; the fault is in the zone itself.
How a receiver handles it is entirely up to the receiver. In practice Google and Microsoft treat permerror like "no SPF": the message gets no authentication credit, and if DMARC depends on SPF, DMARC fails as well. That is why a permerror is worse than a missing record: you believe you are protected, but you are not.
Cause 1: More than ten DNS lookups
By far the most common cause. A receiver may perform at most ten DNS queries while evaluating the record. Every include, a, mx, ptr, exists and redirect counts, including the lookups that the included records themselves trigger. Microsoft 365 costs two, Google Workspace three, a typical newsletter tool two to four. Add a CRM, a ticket system and the old hosting provider, and the limit is gone.
The fix is cleaning up, not tricks: remove services that no longer send. Replace includes of services with fixed IP addresses by ip4 or ip6 mechanisms, which cost no lookup. If that is not enough, spread senders across subdomains (newsletter.example.com with its own SPF), because every domain has its own limit.
v=spf1 include:spf.protection.outlook.com include:_spf.google.com include:sendgrid.net include:mail.zendesk.com include:servers.mcsv.net include:spf.old-host.example -all
Result: 13 lookups, permerror. After cleaning up:
v=spf1 include:spf.protection.outlook.com include:sendgrid.net ip4:203.0.113.10 -allCause 2: Multiple SPF records
A domain may have exactly one TXT record starting with v=spf1. Two of them are a permerror by RFC, regardless of content. This happens when a provider's guide says "add this TXT record" and the client takes it literally instead of extending the existing record.
Check all TXT records of the domain and merge every mechanism into a single record. Delete the rest.
Cause 3: Syntax errors
One typo is enough. Typical cases: "include=" instead of "include:", a space inside a mechanism, "v=spf1" not at the start, a line break or quotation mark in the middle of the record because the DNS editor split a long string, or a mechanism that does not exist ("ipv4:" instead of "ip4:").
Strings longer than 255 characters are especially tricky: DNS only allows them as several sub-strings that the receiver joins again. Some DNS panels split them in the wrong place, leaving a quotation mark in the middle of a domain name.
- Every mechanism with a colon: include:, ip4:, ip6:, a:, mx:, exists:
- Exactly one space between mechanisms, no tabs, no commas
- The all term exactly once and at the very end
- Upper case is not required, but it does no harm either
Cause 4: Include pointing to a domain without SPF
include:spf.service.example only works if spf.service.example has an SPF record itself. If it is missing because the service moved, changed its hostname or the client mistyped it, the whole domain gets a permerror. One orphaned include is enough.
The SPF checker resolves every include and shows which targets return no record. Remove such entries or replace them with the current hostname from the provider's documentation.
Cause 5: Redirect loops and ptr
Rarer, but stubborn: a redirect= pointing to a domain that points back, or an include that eventually includes itself. The receiver aborts and reports permerror. The obsolete ptr mechanism does not cause a permerror, but many receivers ignore it and it costs lookups; remove it.
Step by step
The order matters, because one error can hide the next.
- Fetch all TXT records of the domain and make sure exactly one starts with v=spf1.
- Run the record through the SPF checker: syntax, lookup count and orphaned includes are shown.
- Remove services that no longer send. If unsure, ask the client which systems sent mail in the last twelve months.
- Switch static senders to ip4/ip6, move bulk senders to subdomains.
- Publish the new record with -all or, for now, ~all, keep the TTL short and re-check after five minutes.
- Watch the DMARC reports of the following days: they show whether all legitimate senders now pass.
Frequently asked questions
- Can I get around the lookup limit with SPF flattening?
- Flattening replaces all includes with the resolved IP addresses. It works until a provider changes its addresses, which Microsoft and Google do regularly. Without automation, flattening is a ticking time bomb. Cleaning up is almost always the better solution.
- How long until the fixed record takes effect?
- As long as the TTL of the old record, usually between five minutes and one hour. Receivers do not cache SPF beyond that.
- Why does one tool show permerror and another pass?
- Some tools count lookups differently or ignore syntax errors. What matters is the behaviour of large receivers, and they follow RFC 7208 strictly. When in doubt, look at the DMARC aggregate reports from Google.