SPF has seven possible results. Most people can name three. The distinctions between the other four are where diagnosis lives, because two of them are your problem and two of them are emphatically not.
The seven
| Result | Means | Whose problem |
|---|---|---|
| pass | A mechanism matched with a + qualifier. | — |
| fail | Matched with -. The domain says this host is not authorised. | Yours, if the host was legitimate. |
| softfail | Matched with ~. Not authorised, but do not reject on my account. | Yours, same as fail. |
| neutral | Matched with ?, or no mechanism matched and there was no redirect. | Yours. You have published a record that asserts nothing. |
| none | No record published, or no usable domain to check. | Yours — the control is simply absent. |
| permerror | The record cannot be evaluated as written. | Yours. Always. |
| temperror | A transient failure, effectively always DNS. | Usually not yours. |
neutral and none are not the same, and both are worth finding
none means nothing was published. neutral means something was published and it declines to say anything — a ?all, or a record whose mechanisms all missed. They arrive at the same place: RFC 7208 §8.2 says a receiver must treat neutral exactly as it treats none.
The difference is diagnostic. none is a domain nobody has configured. neutral is a domain somebody configured badly, which is a different conversation.
fail against softfail
Under DMARC the distinction disappears: both are simply not-a-pass, and the DMARC policy decides what happens. Outside DMARC, receivers treat a bare fail more harshly than a softfail, and neither reliably causes rejection on its own.
Which means the practitioner answer to "should we move to -all" is a question back: are you at DMARC enforcement? If yes, it changes very little. If no, it is the strongest signal you have, and worth having once your inventory is complete.
The two errors are the whole game
- permerror is always yours. Two records, malformed syntax, an unknown mechanism, the lookup budget, or more than two void lookups. It is a hard failure: no host can pass, including the ones you listed explicitly. A domain sitting at permerror has SPF switched off without knowing it.
- temperror is usually not yours. A resolver timed out, a nameserver was briefly unreachable, a UDP response was lost. Receivers are expected to defer rather than reject. It becomes yours only if it is persistent — which points at your nameservers or at an included domain’s, not at the record text.
Telling those two apart from a report is the subject of module 7, and the first move is always the same: permerror means read the record, temperror means read the DNS.