Blog · Spf
What Does an SPF Result Mean? Pass, Fail, Softfail, Neutral, None, Temperror and Permerror Explained
What Does an SPF Result Mean? Pass, Fail, Softfail, Neutral, None, Temperror and Permerror Explained
SPF has exactly seven defined results, listed in RFC 7208 section 2.6. Only one of them, pass, counts towards a DMARC pass, and only when the authenticated domain is aligned with the From: domain. The other six all mean the same thing to DMARC, which is "SPF did not authenticate this domain", but they do not mean the same thing to you.
Four of them get misread more often than the rest, so here is the short version of each. A softfail means the domain owner believes the host is probably not allowed but has deliberately declined to say so strongly. A permerror means the record cannot be read at all, and only you can fix it. A none usually means there is no SPF record for that domain. A neutral means the publisher made no claim either way, and receivers treat it exactly like none.
Underneath all seven is a split into three families: a verdict (pass, fail, softfail), an absence of information (none, neutral) and an error (temperror, permerror). The family decides who has to act and whether waiting will help.
Read each result name as a sentence and the whole set becomes obvious. Pass means yes. Fail means no. Softfail means probably no, but I will not commit. None means nobody asked. Neutral means nobody answered. Temperror means ask again later. Permerror means stop, until somebody fixes the record.
The seven SPF result codes at a glance
| Result | What the verifier concluded | DMARC counts it? | What to do next |
|---|---|---|---|
pass | The sending host is authorised to use that domain | Yes, if the domain is aligned | Nothing for SPF. Move on to alignment and DKIM |
fail | The sending host is explicitly not authorised | No | Add the sender to the record, or find out why it is sending |
softfail | The publisher thinks the host is probably not authorised, but will not say so strongly | No | Add the sender, or decide whether the record can move to -all |
none | No SPF records were retrieved, or no usable domain could be extracted | No | Publish a record, or check that you are looking at the right domain |
neutral | The publisher explicitly made no assertion | No | Replace the ?all with a real policy if the domain is yours |
temperror | A transient DNS error interrupted the check | No | Wait and check again. Look at DNS health, not at the record |
permerror | The published records could not be interpreted at all | No | Fix the record. This is the only result that always needs an edit |
Two entries in that table are worth pausing on. none and neutral look similar in the column but not in practice: none is almost always an absent record, while neutral is a record that exists and deliberately says nothing. And temperror and permerror both read as "something broke", yet one resolves itself and the other does not.
For the record-side half of this work, DMARCFlow's SPF Record Checker at https://dmarcflow.com/en/spf-checker reports the published record, the result and the configuration problems behind it, so the permerror causes and the missing-record case are visible without hand-editing a query.
The three families: verdict, absence of information, error
The seven results are not seven flavours of the same thing. They are three categories, and the category decides your next move.
Take a real example. A mail server sends your newsletter. The receiving server looks up your SPF record and gets back softfail. You did not get a broken DNS zone, you did not get a stranger's mailbox, you got a verdict about your own server. That verdict is a policy decision you made, and the only party who can change it is you. The same lookup against a domain with no SPF record returns none, and none is not a verdict at all. There is no decision to undo, only a record that was never published.
A verdict means the verifier reached a conclusion about the sending host. Pass, fail and softfail are verdicts, and you decide what to do about them. A softfail is a weaker verdict than a fail, but it is still a verdict.
An absence of information means nobody said anything. None and neutral are in this family. The domain owner either published nothing, or published something that explicitly declines to assert anything. There is no authorisation decision to act on here, because no decision was made.
An error means the verifier could not complete the check. Temperror and permerror are in this family, and the difference between them is the difference between a problem that resolves itself and a problem that will not resolve until somebody edits a DNS record.
The practical rule: a verdict is fixed by your policy, an absence of information is fixed by publishing a policy, and an error is fixed by fixing the thing that broke.
Which SPF result does DMARC accept?
Only pass. And only when the domain is aligned.
There are two separate conditions here, and it is worth separating them because people conflate them constantly.
First, the SPF result itself. DMARC takes the result of SPF authentication of the MAIL FROM identity, as RFC 7489 section 4.1 states. But DMARC only consumes authenticated domains from authentication technologies "when they succeed", as section 3.1 puts it. Every other SPF result produces no authenticated domain, so it contributes nothing.
Second, alignment. A DMARC pass needs the From: domain to line up with an authenticated domain. In relaxed mode, which is the default, the SPF-authenticated domain and the From: domain must share an organisational domain. In strict mode they must match exactly. That is RFC 7489 section 3.1.2, and section 6.3 gives the default as aspf=r. It is why spf=pass can still sit next to dmarc=fail in the same header.
So the rule in one line: spf=pass plus alignment contributes to a DMARC pass. spf=pass without alignment contributes nothing, and every other result contributes nothing.
SPF and DKIM are alternatives, not a pair
Here is the part that saves people days. An SPF result that is not pass says nothing about DKIM. The two mechanisms are alternatives, and DMARC needs only one of them.
It is "considered to be a DMARC 'pass' if any DKIM signature is aligned and verifies", as RFC 7489 section 3.1.1 says. And the registry entry for the DMARC result confirms it: dmarc=pass means "A DMARC policy record was published for the aligned identifier, and at least one of the authentication mechanisms passed."
That is why a broken SPF record does not automatically break DMARC. A domain with healthy aligned DKIM keeps passing DMARC even if its SPF support has rotted. The breakage shows up the day someone adds an unsigned sender, or a DKIM key rotates, or a stricter receiver starts enforcing.
What lands in a DMARC failure report
The SPF result matters twice: once for the verdict the receiver reaches, and once for what you get told about it later.
By default, a DMARC failure report is generated when "all underlying authentication mechanisms fail to produce an aligned 'pass' result". That is the fo=0 setting in RFC 7489 section 6.3. Change it to fo=1 and the report is generated when "any underlying authentication mechanism produced something other than an aligned 'pass' result".
The difference is not cosmetic. On a default fo=0 record, a message with spf=softfail and a passing aligned DKIM generates no failure report at all, because the pair still produced an aligned pass. On an fo=1 record, the same message is reported, and you get to see the softfail.
That is the practical reason to turn on fo=1 while you are still finding your sending sources. It turns the SPF result into something you can act on even when DKIM is carrying your DMARC score.
DMARC has its own result set, and it has five values, not seven
This trips people up constantly, because the two result sets use the same words for different things.
| DMARC result | Meaning |
|---|---|
dmarc=none | No DMARC policy record was published for the aligned identifier, or no aligned identifier could be extracted |
dmarc=pass | A DMARC record was published for the aligned identifier and at least one authentication mechanism passed |
dmarc=fail | A DMARC record was published for the aligned identifier and none of the authentication mechanisms passed |
dmarc=temperror | A temporary error occurred during DMARC evaluation. A later attempt might produce a final result |
dmarc=permerror | A permanent error occurred during DMARC evaluation, such as a syntactically incorrect DMARC record |
Five values, from RFC 7489 section 11.2. There is no dmarc=softfail and no dmarc=neutral, because DMARC does not have soft verdicts. It either found an aligned identifier that passed or it did not.
So spf=none and dmarc=none are completely different statements. The first says there is no SPF record for the envelope domain. The second says there is no DMARC record for the aligned identifier. If you see both in one header, the domain has neither.
The policy that turns these results into an action is worth checking directly, and DMARCFlow's DMARC Checker at https://dmarcflow.com/en/dmarc-checker shows the published record, the policy level and the alignment status for a domain, which is what tells you whether DMARC was even in a position to act on the SPF result you were reading.
What does each result prove, and what does it not prove?
An SPF result is a statement about authorisation. It is not a statement about the message.
SPF authenticates the envelope domain, not the From header
SPF checks the MAIL FROM identity. DMARC uses "the result of SPF authentication of the MAIL FROM identity", per RFC 7489 section 4.1. The From: header is a different domain entirely, and the relationship between the two is what alignment is about.
The practical consequence: a clean SPF pass tells you the sending host is allowed to send for the envelope domain. It tells you nothing about who the message claims to be from. That gap is exactly what DKIM and DMARC alignment close.
The domain the check ran against is not the domain in the From header
The Authentication-Results header names the domain the check was performed against. RFC 7208 section 9.2 shows the shape of it, with the smtp.mailfrom= parameter carrying the envelope domain:
Authentication-Results: myhost.example.org; spf=pass smtp.mailfrom=example.net
When a message comes from a mailing list, a forwarder or a bulk sender, the envelope domain is often the provider's domain rather than the author's. The SPF result you are reading was computed against that envelope domain, which is why it can be a pass while DMARC still fails on alignment.
Pass is not a reputation score
RFC 7208 section 8.3 says that on a pass, "the domain can now, in the sense of reputation, be considered responsible for sending the message." Read that narrowly. It is about responsibility for the message, not a judgement about quality, and it is not a statement about spam.
An SPF pass does not check the content. It does not check whether a person sent the message. It does not tell you where the message will land. And RFC 7208 section 2.6 opens by saying the protocol "establishes no normative requirements for handling any particular result", so what a receiver does with your result is local policy, not a rule you can look up.
Softfail: a deliberate non-statement, not a weak fail
Softfail is the result people read most often and understand least.
RFC 7208 section 2.6.5 defines it: "A 'softfail' result is a weak statement by the publishing ADMD that the host is probably not authorized. It has not published a stronger, more definitive policy that results in a 'fail'."
Read that again. The domain owner thinks the host is probably not allowed, and has chosen not to publish a strong policy saying so.
The mechanism behind it is in RFC 7208 section 4.6.2. A qualifier in front of a mechanism decides the result when that mechanism matches:
"+" pass
"-" fail
"~" softfail
"?" neutral
The qualifier is optional and defaults to +. So ~all produces a softfail, -all produces a fail, ?all produces a neutral, and a bare all produces a pass. That is the whole mechanism behind three of the seven results, and it is why ~all is the signature of a softfail.
The handling guidance is in section 8.5, and it is the part most articles leave out:
A "softfail" result ought to be treated as somewhere between "fail" and "neutral"/"none". The ADMD believes the host is not authorized but is not willing to make a strong policy statement. Receiving software SHOULD NOT reject the message based solely on this result, but MAY subject the message to closer scrutiny than normal.
So a softfail is not a fail that got waved through. It is closer to the opposite: a fail is a confident no, a softfail is a shrug. Receivers are told not to reject on it alone, though they may look harder at the message.
What to do depends on whose record it is. If it is your domain and the record ends in ~all, the decision is whether you can finish enumerating your senders and move to -all. If it is someone else's domain, their record is not yours to change, and the only lever you have is DKIM on your own sending side.
Neutral and none: two words for the same thing to a receiver
Here is the rule that saves an afternoon of confusion: a neutral result must be treated exactly like a none result. RFC 7208 section 8.2 says it in those words, and adds why: "the distinction exists only for informational purposes."
The definitions are still different, and worth knowing.
none (section 2.6.1) means either that no syntactically valid domain name could be extracted from the SMTP session, or that no SPF records were retrieved from DNS. In practice, for a domain you control, it almost always means the second: there is no SPF record published for that domain. A none result is not a broken record. It is an absent one.
neutral (section 2.6.2) means the domain owner explicitly stated they are not asserting whether the sending IP is authorised. That is the ?all mechanism.
The default result is neutral, not fail
This one surprises people. RFC 7208 section 4.7:
If none of the mechanisms match and there is no "redirect" modifier, then the check_host() returns a result of "neutral", just as if "?all" were specified as the last directive.
So neutral arrives two ways: explicitly, from ?all, and implicitly, when the record simply does not end in an all mechanism and nothing matched. A record like v=spf1 mx that does not match the sender produces a neutral, not a fail.
If you are looking at a record that genuinely ends in -all and still seeing a neutral, the record you are reading is not the record that was evaluated. Check that you are looking at the right domain, because a subdomain does not inherit its parent domain's record.
Temperror and permerror: the two errors that need different responses
This is the pair where the difference is the whole point.
temperror means the verifier hit a temporary problem. RFC 7208 section 2.6.6 describes it as a transient error, usually on the DNS side, that interrupted the check, and adds the part that matters for your next move: "A later retry may succeed without further DNS operator action." Section 4.4 is more specific about what triggers it: a DNS server failure, any other DNS error, or a lookup that times out.
permerror means the record is broken. Section 2.6.7: "the domain's published records could not be correctly interpreted. This signals an error condition that definitely requires DNS operator intervention to be resolved."
One of those is a nuisance. The other is a to-do list.
What causes a permerror in practice
Three causes cover nearly every case.
More than ten DNS-querying terms. RFC 7208 section 4.6.4 requires implementations to cap the terms that cause DNS queries at ten, and states that if the limit is exceeded, "the implementation MUST return 'permerror'". The terms that count are the include, a, mx, ptr and exists mechanisms and the redirect modifier. The all, ip4 and ip6 mechanisms and the exp modifier do not query DNS and are not subject to the limit. Every include: you add to pull in another provider costs one of your ten, and the DNS lookups inside that included record count against the same ten.
Two or more SPF records on the same domain. Section 4.5 is blunt about it: if the record set includes more than one record, check_host() produces the permerror result. Section 3.2 states that a domain name MUST NOT have multiple records that would cause an authorization check to select more than one record. Two separate v=spf1 TXT records is not "first one wins". It is a permerror, and the receiver cannot evaluate either of them.
A syntax the verifier cannot parse. An unknown mechanism, a malformed modifier or a broken character can make the record unreadable. Section 8.7 also notes that macros can produce a permerror when the checked identity has an unexpected format.
In all three cases the effect is the same: the record is not evaluated, so the SPF result cannot be a pass.
The SMTP reply code tells you which error you have
You do not have to guess from the result name. RFC 7208 section 8 tells receivers what reply code to use if they reject, and the codes separate the three cases cleanly:
| Result | Reply code if the receiver rejects | Enhanced status code |
|---|---|---|
fail | 550 | 5.7.1 |
temperror | 451 | 4.4.3 |
permerror | 550 | 5.5.2 |
So a 451 with a 4.4.3 means wait, it is a transient condition and the receiver is asking for a retry. A 550 with a 5.5.2 means the record has to change before anything works.
For contrast, a DMARC rejection looks different again. RFC 7489 section 10.3 gives the example 550 5.7.1 Email rejected per DMARC policy for example.com, and notes that a receiver which cannot retrieve or apply the DMARC policy should defer with a 4xy code.
Where to read the result
The Authentication-Results field
Results are reported in the Authentication-Results header field, one field per receiving server, with the result for that server's own check. RFC 7208 section 9.2 notes that this field "provides less information than the Received-SPF field", so if you need the full picture of one check, the Received-SPF field carries more detail about the same evaluation.
Two things to read off it. The spf= value is the result itself. The smtp.mailfrom= parameter names the MAIL FROM address the check ran against, and its domain part is the one SPF authorised. That is the fact that tells you whether you are even looking at the right domain.
How to check your own domain and your own messages
There are two different questions here and two different checks.
For the record side, DMARCFlow's SPF Record Checker at https://dmarcflow.com/en/spf-checker looks up the published record for a domain and reports the result along with the configuration problems behind it, including the lookup count and duplicate records. That is the check that answers the permerror questions, and the check that tells you whether a none result means no record or a record on somebody else's domain.
For the message side, DMARCFlow's Email Header Analyzer at https://dmarcflow.com/en/email-header-analyzer takes the raw headers of a message you actually received and parses the Authentication-Results field into SPF, DKIM and DMARC results with plain-language explanations, alongside the domain the check was performed against. No account is needed and headers are not stored.
To be clear about the limits of both: they report what is published and what a header says. They do not change DNS, they do not repair a record, and they do not decide where a message lands.
FAQ
What does spf=softfail mean?
The sending host is probably not authorised for that domain, but the domain owner has not published a strong enough policy to say so outright. It is the signature of a record ending in ~all. Receivers are told to treat it as somewhere between fail and neutral, and not to reject on it alone.
Is softfail treated the same as fail?
No. A fail is an explicit statement that the host is not authorised. A softfail is a weak statement that it is probably not authorised. RFC 7208 section 8.5 says a softfail ought to be treated as somewhere between fail and neutral/none, and that receiving software should not reject the message on that result alone.
What is the difference between spf=temperror and spf=permerror?
A temperror is transient. A retry may succeed without anyone changing DNS. A permerror means the published records could not be interpreted and definitely need a DNS operator to intervene. If a receiver rejected, a temperror uses 451 with a 4.4.3 enhanced code and a permerror uses 550 with a 5.5.2.
Does spf=none mean my SPF record is broken?
Usually it means there is no SPF record for that domain. The result covers both "no records were retrieved from DNS" and "no usable domain could be extracted". Check whether a record exists for that exact domain, including whether you are looking at a subdomain, which does not inherit the parent domain's record.
Why do I get spf=neutral when my record ends in -all?
You should not. If the record really ends in -all and the sender does not match, the result is a fail. A neutral means either that the record does not end in an all mechanism, since an unmatched record with no all mechanism ends as neutral by default, or that you are reading a different domain's record than the one that was evaluated.
Which SPF result does DMARC accept?
Only pass, and only when the authenticated domain is aligned with the From: domain. In relaxed mode that means the same organisational domain, in strict mode an exact match. Every other SPF result contributes nothing to a DMARC pass.
If SPF fails, does DMARC fail too?
Not necessarily. DMARC passes when either SPF or DKIM is aligned and passes. An SPF failure says nothing about DKIM, so a domain with healthy aligned DKIM can keep passing DMARC even while its SPF support is broken.
What is the difference between dmarc=none and spf=none?
They are different result sets with different meanings. spf=none means no SPF record was retrieved for the envelope domain. dmarc=none means no DMARC record was published for the aligned identifier, or no aligned identifier could be extracted. DMARC has five results of its own: none, pass, fail, temperror and permerror.
What causes an SPF permerror?
Most often more than ten DNS-querying terms in the record, two or more v=spf1 records on one domain, or a syntax the verifier cannot parse. In every case the record is not evaluated at all, so the result cannot be a pass.
How many SPF records can a domain have?
One SPF record, meaning one TXT record starting with v=spf1. RFC 7208 section 3.2 says a domain name must not have multiple records that would cause an authorization check to select more than one record, and section 4.5 makes a record set with more than one such record a permerror. Other TXT records on the same domain are fine, including one SPF record split across several quoted strings. If you need several senders, chain them into one record with include: statements.