21 June 2026 · 12 min read
A failing SPF record is one of the most common reasons legitimate email gets marked as spam, fails DMARC, or gets rejected outright. The frustrating part is that "SPF is failing" can mean several very different things, and the fix for one cause will not touch another. A permerror caused by too many DNS lookups needs a completely different remedy from a plain fail caused by a missing sender, which in turn has nothing to do with a temperror that clears itself an hour later.
This article walks through how to read the actual SPF result your receivers are returning, how to map that result to a root cause, and how to fix each one cleanly without breaking the senders that are already working. We will cover the difference between permerror, temperror, softfail and fail, the ten-lookup limit that quietly breaks a huge proportion of SPF records, the problem of having two SPF records on one domain, missing or wrong include statements, and when SPF flattening is the right answer (and when it is a trap).
If you want to follow along against your own domain, run the SPF Checker first so you have your live record and lookup count in front of you. Everything below assumes you can see what your published record actually resolves to right now, not what you think it should be.
First, work out which kind of failure you have
"SPF failed" is not a diagnosis. When a receiver evaluates SPF it returns one of seven defined results, and you must know which one you are getting before you change anything. Guessing leads to editing the record at random, which usually makes things worse.
The seven SPF results from RFC 7208 are: pass, fail, softfail, neutral, none, permerror and temperror. The three you actually need to worry about when something is broken are permerror, temperror and fail (with softfail and none as honourable mentions). Here is what each one is telling you.
passmeans the sending IP is authorised by your record. Nothing to fix.fail(a hard fail, from-all) means SPF evaluated correctly but the sending IP is not listed. The record works; the sender is not in it. This is an authorisation problem, not a syntax problem.softfail(from~all) means the same thing asfailbut you have told receivers "treat unlisted senders with suspicion rather than rejecting them". Mail usually still gets delivered, often to spam.neutral(from?all) means you have explicitly declined to assert anything. SPF is effectively switched off for unlisted senders.nonemeans there is no SPF record at all, or no record that applies. DMARC cannot use SPF for alignment in this state.permerrormeans a permanent error: the record is malformed, there are two records, or you have exceeded the ten-lookup limit. The receiver cannot evaluate SPF at all, and most treat this as no usable result.temperrormeans a temporary DNS error: a lookup timed out or a server was briefly unreachable. It usually clears on retry.
You can see your live result in two places: in the Authentication-Results header of any message you have received (search it for spf=), and in your DMARC aggregate reports, where every sending source is tagged with its SPF result. If you are not yet collecting those reports, the DMARC Report Analyzer will parse them for you and group senders by pass and fail, which makes the pattern obvious very quickly.
The rest of this article is organised by result. Find yours and go straight to it.
Fixing a permerror: the most common real failure
A permerror is the one people most often mean when they say "my SPF record is broken", because unlike a plain fail it breaks SPF for every sender, even the ones you correctly listed. There are three causes, in rough order of how often they bite.
Cause 1: you have exceeded the ten-lookup limit
This is the big one. RFC 7208 caps the number of DNS-querying mechanisms a receiver has to resolve when evaluating your record at ten. Go over ten and the result is an immediate permerror, regardless of whether the actual sender would have passed. See the SPF 10-lookup limit entry for the formal definition.
The mechanisms that cost a lookup are:
includeamxptr(deprecated, avoid it entirely)- the
redirectmodifier
The mechanisms that cost nothing are ip4, ip6 and all, because they need no DNS query to evaluate.
The trap is nesting. Every include you add does not just cost one lookup; it costs one lookup plus whatever lookups the included record itself contains. A single include:_spf.google.com expands internally to several more includes. Add Microsoft 365, a marketing platform, a CRM and a help-desk tool, and you are well past ten before you have finished, even though your published record only shows four or five include statements.
To fix it:
- Run the SPF Checker. It counts the resolved lookups for you and shows exactly which mechanisms contribute. Do not count by hand; nested includes make manual counting unreliable.
- Remove
includestatements for services you no longer use. Old marketing tools and abandoned trials are the usual culprits. Every removed vendor frees up its whole nested chain. - Replace any
aormxmechanism that exists only to authorise a known, static IP. If your mail server has a fixed address,ip4:203.0.113.10costs zero lookups whereaormxeach cost one. - Eliminate every
ptrmechanism. It is deprecated, slow, and counts against your limit. - If you are still over ten after genuine cleanup, consider flattening (covered in its own section below).
There is a related rule that catches people: the void lookup limit. If more than two of your lookups return nothing (an NXDOMAIN or an empty answer), receivers also raise a permerror. This usually means you are include-ing a vendor whose SPF record has since been removed or whose domain no longer resolves. The void lookup entry explains it in full. Audit your includes and delete any that point at a dead host.
Cause 2: you have two SPF records
A domain may publish exactly one SPF record. The standard is explicit: if a receiver finds more than one TXT record beginning with v=spf1, the correct result is permerror. Receivers will not "merge" them or pick the better one; they fail.
This happens constantly when a second vendor is onboarded. The vendor's setup guide says "add this TXT record", someone pastes a second v=spf1 include:vendor.example -all alongside the existing one, and SPF silently breaks for the whole domain.
The fix is to merge the two records into one. Take the mechanisms from both and combine them into a single v=spf1 ... all string. For example, if you have:
v=spf1 include:_spf.google.com -all
v=spf1 include:spf.protection.outlook.com -all
you replace both with one record:
v=spf1 include:_spf.google.com include:spf.protection.outlook.com -all
Keep exactly one all mechanism, at the end. After merging, re-run the SPF Checker to confirm only one record is found and that the merge has not pushed you over the lookup limit.
Cause 3: a syntax error in the record
Less common, but worth ruling out. Typical mistakes are a stray space inside a mechanism, a missing colon (includegmail.com instead of include:_spf.google.com), an unknown mechanism name, a CIDR mask that is out of range, or text wrapping that has inserted a line break into the published TXT value. Any of these can yield a permerror. The SPF Checker will flag the offending token. Fix the typo, republish, and re-test.
Fixing a temperror: usually not your record
A temperror (temporary error) means the receiver could not complete a DNS lookup while evaluating your record: a timeout, a SERVFAIL, or a transient network problem. Importantly, this is often not a fault in your record at all, and it frequently clears itself on the next attempt.
Before you change anything, retry. Wait a few minutes and run the SPF Checker again. If the result has flipped to pass, you were looking at a transient blip on your DNS provider's side or the receiver's resolver, and there is nothing to fix.
If temperror is persistent, the usual causes are:
- A slow or flaky authoritative DNS server, either yours or that of a vendor you
include. Each nested include adds another server that has to answer in time. Hosting your zone on fast, reliable nameservers (Cloudflare being the obvious example) removes a lot of these. - DNSSEC misconfiguration somewhere in the chain, which can make resolvers return SERVFAIL. Check it with the DNS Propagation Checker to confirm your records resolve consistently across resolvers.
- An included vendor's DNS being overloaded. You cannot fix theirs, but flattening that include (resolving it to fixed IPs) removes your dependence on their nameserver being up at evaluation time.
The practical point: do not respond to a one-off temperror by rewriting your record. Confirm it is persistent first, then look at DNS reliability rather than SPF content.
Fixing a fail or softfail: a legitimate sender is missing
If you are getting a hard fail or a softfail, your SPF record is syntactically fine and evaluating correctly. The problem is the opposite of a permerror: SPF is working, and it is correctly telling you that the IP which sent the message is not authorised by your record.
There are two possibilities, and you must tell them apart before acting.
Possibility one: the sender is legitimate and you forgot to authorise it. You started using a new ESP, invoicing tool, CRM, or transactional email service, and never added it to SPF. The fix is to add the correct mechanism, which is almost always the include the vendor specifies in their docs. Add it to your single record, mind the lookup limit, republish, and re-test.
Possibility two: the sender is not legitimate. Someone is sending mail using your domain in the MAIL FROM, and SPF is doing exactly its job by failing them. In that case you do not want to add them. A fail here is a feature, and it is one of the signals DMARC uses to protect your domain from spoofing.
This is precisely why you should never "fix" an SPF fail by loosening the policy or adding senders blindly. Use your DMARC aggregate reports to identify which IPs are failing and decide, source by source, whether each one is yours. The DMARC Report Analyzer groups failing sources so you can see at a glance whether a failing IP belongs to a known vendor or to an unknown spoofer.
A note on softfail versus fail: the ~all (softfail) and -all (hardfail) qualifiers change how receivers treat unlisted senders. -all is the stronger, correct end state once you are confident every legitimate sender is listed. ~all is the safer setting while you are still discovering senders, because unlisted mail is treated with suspicion rather than rejected. The SPF all qualifier entry covers each qualifier. The mistake to avoid is leaving ?all (neutral) or +all (pass everything) in place, because both effectively disable SPF as a protection.
When SPF flattening is the right answer (and when it is not)
If you have done a genuine audit, removed dead and unused includes, converted static a/mx mechanisms to ip4, and you are still over the ten-lookup limit because you legitimately use a lot of senders, then SPF flattening is the tool for the job.
Flattening means resolving your include mechanisms down to the actual ip4 and ip6 ranges they expand to, and publishing those ranges directly. Because ip4 and ip6 cost zero lookups, a flattened record can authorise dozens of sources while staying well under ten. The SPF flattening entry covers the concept in detail.
The catch, and it is a serious one, is that flattening freezes a moving target. Large senders like Google and Microsoft change their published IP ranges. The whole point of their include is that they maintain the list for you. The moment you flatten that include into static IPs, you become responsible for keeping those IPs current, and the day they add a new outbound range that you have not copied in, your mail from that sender starts to fail.
So the rule is:
- Do not flatten providers that publish a maintained
includeand change their IPs (Google Workspace, Microsoft 365, most large ESPs), unless you have automation that re-resolves and republishes the flattened record whenever their ranges change. - Do flatten small or static senders whose IPs genuinely never move, where the
includeexists only to wrap one or two fixed addresses. - Never hand-flatten once and forget. A manually flattened record is a deliverability incident waiting to happen.
If you are flattening to solve a real lookup-limit problem, you need it kept in sync automatically. This is exactly what a managed setup handles: our SPF management keeps a flattened record valid by re-resolving sources and republishing as their ranges change, so you get under the limit without the staleness risk. For the underlying concepts, the SPF guide and the SPF product page lay out the full approach.
If your immediate need is just to split an over-long TXT value safely or to validate a record you have flattened by hand, the DNS Record Splitter and the SPF Checker will keep you honest.
A repeatable fix-it checklist
When SPF is failing and you want a reliable order of operations rather than trial and error, work through this list top to bottom:
- Identify the exact result. Read
spf=in a message header or in a DMARC aggregate report. Is itpermerror,temperror,fail,softfailornone? Do not proceed until you know. - Confirm there is exactly one record. Two
v=spf1TXT records is an instantpermerror. Merge them into one. - Count your lookups with a tool. Run the SPF Checker. If you are at or over ten, you have found the
permerrorcause. - Remove dead weight. Delete unused vendor includes, kill every
ptr, convert statica/mxtoip4/ip6, and remove includes that return void lookups. - Add genuinely missing senders. For each legitimate source returning
fail, add the vendor's documentedinclude. Cross-check against DMARC reports so you are adding yours and not a spoofer. - Flatten only what must be flattened, and only with a way to keep it in sync.
- Set the right
allqualifier. Use~allwhile discovering senders, move to-allonce the list is complete. Never+allor?all. - Re-test after every change and let DNS propagate before drawing conclusions. The DNS Propagation Checker shows whether your edit has actually gone live everywhere.
SPF is one leg of three: do not fix it in isolation
It is worth remembering why you are doing this. SPF on its own authorises the MAIL FROM domain, but a spoofer can pass SPF while still forging the visible From address a recipient sees. That gap is closed by DMARC alignment, which requires the domain that passes SPF (or DKIM) to match the visible From domain. Fixing SPF so it returns pass is necessary, but it only protects you when DMARC ties that pass back to the address your recipients actually read.
So once SPF is clean and passing for your legitimate senders, confirm the whole chain:
- Re-run the SPF Checker for a clean
passand a lookup count under ten. - Check DKIM is signing and aligned with the DKIM Checker.
- Check your policy with the DMARC Checker, and make sure SPF or DKIM aligns for every legitimate source before you move the policy towards
p=reject.
If you would rather not babysit lookup counts, vendor IP changes and two-record accidents by hand, that ongoing maintenance is what DMARC Engine is built to do. We monitor your SPF record continuously, so a newly onboarded vendor never quietly pushes you over the ten-lookup limit, a flattened record never goes stale, and a second v=spf1 entry is caught before it breaks delivery. The free tools above will diagnose a failing record today; the platform keeps it from failing again tomorrow. To understand how SPF fits the safe path from p=none to p=reject, read the requirements overview and the SPF product page.