SPF Enforcement Postures
SPF enforcement postures
Cathedra’s SPF check runs in two halves. The first half is presence and uniqueness — does the record exist, and is it the only one. The second half grades the enforcement posture, which is everything that depends on how the record ends.
This lesson is the second half. Every finding Cathedra emits for posture is below, quoted verbatim from SpfRecordCheck.php. Memorize them. The point of this course is that you can predict the report before the scan finishes.
How Cathedra decides
The check loads the SPF record’s text and runs four str_contains tests in order:
- Does the record contain
-all? - Does it contain
~all? - Does it contain
+allor?all? - None of the above — no explicit
allmechanism.
Only the first match counts. The branches are exclusive. Each branch emits one finding with one specific status, severity, and message.
Branch 1 — -all (hard fail)
The enforcing posture. Anything not explicitly authorized is rejected by the receiver.
Cathedra status: pass
Finding text (verbatim):
SPF ends with -all (hard fail). Unauthorized senders are rejected.
Recommendation: none. This is the desired state.
When you see this, the record is doing its job. Move on to the lookup-limit check (lesson 3.4) and confirm the count is under ten.
Branch 2 — ~all (soft fail)
The transitional posture. Unauthorized senders are flagged but usually still delivered, typically to spam. Receivers treat soft-fail conservatively — most deliver it, some quarantine, none reject outright.
Cathedra status: warning
Severity: medium
Finding text (verbatim):
SPF ends with ~all (soft fail). Unauthorized senders may still be delivered.
Recommendation (verbatim):
Change to -all once your SPF record is verified to be complete.
This is the most common posture you will see in the wild and the most common one you will be asked to fix. Clients almost always start at ~all because a previous provider was nervous about hard-failing legitimate mail.
The pattern for moving a client from ~all to -all:
- Run Cathedra. Confirm the record is
~all. - Open DMARC reporting if it exists. If
rua=is configured, you can read the aggregate reports and confirm no legitimate senders are being soft-failed. - If no DMARC reporting exists, deploy DMARC at
p=nonewithrua=first (Module 5). Run it for two to four weeks. - Read the aggregate reports. Confirm every legitimate sender appears in the SPF record.
- Change
~allto-all. Re-run Cathedra. Status flips to pass.
Do not flip from ~all to -all without that confirmation step. It is the difference between a clean modernization and a Monday morning call from a furious operations director.
Branch 3 — +all or ?all
Both of these are graded the same way by Cathedra because both have the same practical effect: any server on the internet can send as the domain.
+all is the explicit “allow everyone” qualifier. It is almost always a mistake — someone meant -all and typed the wrong character, or pasted in a record from somewhere they shouldn’t have.
?all is “neutral” — the receiver should make no SPF-based decision. Functionally that is the same as having no SPF at all, but worse, because the presence of the record signals you intended a policy and chose to make it ineffective.
Cathedra status: error
Severity: critical
Finding text (verbatim):
SPF ends with +all or ?all, which allows any server to send as this domain.
Recommendation (verbatim):
Replace with -all immediately.
If you see this finding on a client domain, it is the highest-priority item in the report. A +all record is functionally an invitation to spoof. Open a ticket, contact the client, and replace the record the same day.
Branch 4 — no explicit all
The record exists but has no all mechanism at the end. The RFC default for a missing all is neutral, which means unauthenticated senders get no SPF judgment at all — same effective outcome as ?all.
You usually see this when someone wrote a partial record and forgot the closing rule, or pasted in mechanisms from a guide that omitted the trailing all.
Cathedra status: warning
Severity: medium
Finding text (verbatim):
SPF record does not end with an explicit all mechanism.
Recommendation (verbatim):
Add -all at the end of your SPF record.
The fix is one edit: append -all to the record. If the client has been operating without enforcement, do the same staged confirmation as the ~all → -all migration above.
Cheat sheet
| Record ends with | Cathedra status | Severity | Finding |
|---|---|---|---|
-all |
pass | — | SPF ends with -all (hard fail). Unauthorized senders are rejected. |
~all |
warning | medium | SPF ends with ~all (soft fail). Unauthorized senders may still be delivered. |
+all or ?all |
error | critical | SPF ends with +all or ?all, which allows any server to send as this domain. |
no all mechanism |
warning | medium | SPF record does not end with an explicit all mechanism. |
Print this table. Keep it near your monitor for the first month.
Reading the posture from a record at a glance
Examples you can grade in a second:
v=spf1 include:spf.protection.outlook.com -all
-all. Pass.
v=spf1 include:spf.protection.outlook.com include:sendgrid.net ~all
~all. Warning, medium.
v=spf1 +all
+all. Error, critical. Same-day fix.
v=spf1 include:spf.protection.outlook.com
No all. Warning, medium. Append -all.
v=spf1 include:spf.protection.outlook.com ?all
?all. Error, critical.
The point of this fluency is operational speed. When you are working through a Cathedra report with a client on a call, you should be reading SPF records faster than the page renders.
What you should remember
- Cathedra grades enforcement by what
allqualifier closes the record. -allpasses.~allwarns (medium).+alland?allerror (critical). Noallwarns (medium).- The exact finding text is fixed. You can quote it back to a client before the scan completes.
- Never flip a record from
~allto-allwithout confirming legitimate senders first, ideally through DMARC aggregate reports.
Next: the 10-lookup limit and how a clean-looking SPF record can still fail it.
Cathedra alignment: SpfRecordCheck.php — the enforcement-posture branch and its four exact findings
