SPF softfail vs hardfail (~all vs -all)
Every SPF record ends with an SPF all mechanism, and the character in front of it is the only part most people ever change. The choice looks binary and strict — -all for serious domains, ~all for cautious ones — but that framing is out of date once DMARC is in the picture.
The four endings
| Ending | Name | What you are saying |
|---|---|---|
-all | Fail (hardfail) | Nothing else is authorised. Reject it. |
~all | SoftFail | Nothing else is authorised, but accept and mark it. |
?all | Neutral | I make no statement. |
+all | Pass | Everything is authorised. |
+all authorises the entire internet to send as your domain. It is never correct, and where it appears it is nearly always a typo or a debugging step somebody forgot to remove.
SPF neutral — ?all — is the one people choose when they mean "let me be careful". It does not do that. Neutral tells receivers you have no opinion, which is exactly the information they have when you publish nothing at all. If you want caution, ~all is caution.
That leaves the real decision: ~all or -all.
What receivers actually do
Less than the names suggest. SPF results are inputs to a filtering decision, not commands, and no major receiver rejects on a bare SPF hardfail alone — an SPF failure is one signal among many, and it is a signal known to misfire on ordinary forwarded mail.
In practice:
-allcontributes a strong negative signal. It rarely produces an outright rejection by itself, but it pushes borderline mail toward the spam folder.~allcontributes a weaker one. Mail is accepted and the failure is recorded.
The honest summary: the difference between them is a matter of degree in a spam-scoring decision, not a switch between "delivered" and "rejected".
The part that changes the answer
Under DMARC, the all qualifier stops being the thing that decides the outcome.
DMARC does not read your softfail-versus-hardfail preference. It asks a simpler question: did SPF pass, and was that pass aligned? Anything that is not an aligned pass is a fail as far as DMARC is concerned — softfail, hardfail and neutral are all the same non-pass. What happens next is decided by your DMARC policy, not by the character in your SPF record.
With p=reject published, a message that fails both aligned checks is rejected whether your record ends in ~all or -all. The SPF ending is doing almost none of the work.
This is why the old advice — "you must get to -all to be secure" — is mostly a relic of the pre-DMARC era. Today, moving from p=none to p=quarantine matters enormously; moving from ~all to -all barely registers. If you are choosing where to spend a change window, DMARC policy: none, quarantine, reject is the page that matters.
Which SPF all should I use?
During rollout, use ~all. While you are still discovering senders, a hardfail adds a penalty to mail from systems you have not inventoried yet. SoftFail keeps that mail moving and still records the failure in reports, which is exactly the trade you want at p=none.
Once your senders are known and DMARC is enforcing, -all is the tidier statement. It costs nothing at that point, and it gives a clear answer to receivers that consult SPF outside DMARC — some do, for reputation and for mail with an empty envelope-from, such as bounces.
Never publish ?all or +all.
There is one case where -all earns its keep immediately: a parked domain that sends no mail at all. There, v=spf1 -all plus p=reject is unambiguous and has no legitimate mail to endanger.
A trap worth naming
Tightening ~all to -all while a sender is still missing from the record does not create a new problem — that mail was already unauthorised. What changes is how visibly it fails, which means the complaint arrives after the change and gets attributed to it.
Before tightening, confirm every legitimate source is accounted for. Unknown sending sources exists for this question, and the ten-lookup limit is worth re-checking at the same time, because a record that PermErrors makes the ending irrelevant anyway.
The one-line version
Get DKIM aligned on every sender, publish ~all, raise your DMARC policy, then switch to -all when the reports are clean. Doing it in the other order is how domains lose mail.