SPF Observatory 2026
A standing measurement of what SPF records actually publish across the 320 domains someone ran through the public Humerly audit in the last 90 days. Every figure is read from a pinned corpus file and carries the window, the resolver set and the exclusions that produced it. Rates here are upper bounds, not internet-wide rates: these are domains somebody had a reason to check.
Methodology
Read this before the findings. Every number on this page is read from spf-corpus-2026-09-08.json (sha256 088815037fd3d814…); nothing here is authored, and nothing is derived that the file does not already state.
| Sample | One row per domain: the most recent audit_reports_v2 report for that domain, lowercased. Domains reach this table by someone running them through the public Humerly deliverability audit. |
|---|---|
| Window | last 90 days from measured_at |
| Measured at | 8 September 2026, 14:09 UTC |
| Completed at | 8 September 2026, 14:10 UTC |
| Resolvers | Engine walk: this host's configured recursive resolver. Resolver-disagreement probe: 1.1.1.1, 8.8.8.8, 9.9.9.9 and the zone's authoritative nameserver queried directly. |
| Deduplication | DISTINCT ON (lower(domain)) ordered by created_at DESC. A domain audited nine times would otherwise weight the corpus nine times, and re-audits are not independent: people re-run after a change, so repeats skew toward domains that were failing. |
Exclusions
Rule: rows with source = synthetic_monitor, and these seven
domains: humerly.com, vgcbit.com, jaldok.com, b.jaldok.com, tarniko.com, barniva.farm, bbc.co.uk.
The source exclusion removed ZERO rows: no row in audit_reports_v2 carries source='synthetic_monitor'. Live values are web, outreach and remeasure. The rule is declared because it was specified, and is recorded as inert rather than quietly dropped. Of the seven named domains, six were present in the window; barniva.farm has zero rows in the table at all.
The arithmetic follows from that note: 326 domains in the window, 320 after exclusions — six removed, not seven, because one of the seven named domains had no rows to remove.
Limitations
- Audit visitors are NOT a random sample of the web. These are domains someone had reason to check, so every rate here is an upper bound and must never be quoted as 'N% of domains on the internet'.
- DNS is live state. The corpus rows record what was published when each audit ran; this pass re-measures TODAY, so a domain that fixed or broke its record since is counted as it is now, not as it was.
- Findings are counted per DOMAIN, not per occurrence: a record with three duplicate includes contributes 1 to spf-nested-include-counted-twice.
- Five of the eighteen registry keys have no rule yet and are marked measured=false. They are absent from these counts, NOT zero.
- The engine reads one recursive resolver for the walk itself, so a split-horizon zone is measured as this host sees it.
Engine
| lookup limit | 10 |
|---|---|
| spf findings sha256 | 4092383f9924ad48 |
| spf tree sha256 | d8a0793d5ed8c686 |
| void limit source | RFC 7208 s4.6.4, vendored core/rfc/rfc7208.txt (sha256-verified) |
| void lookup limit | 2 |
What an evaluator returned
One verdict per domain, 320 in total.
| Verdict | Domains | Share | Meaning |
|---|---|---|---|
| pass | 252 | 78.75% | an SPF record was found and evaluated without error |
| fail | 35 | 10.94% | no usable record — see spf-no-record below |
| permerror | 16 | 5.00% | a permanent error: the record exists but cannot be evaluated |
| invalid | 10 | 3.12% | the record was found but did not parse |
| temperror | 5 | 1.56% | a temporary DNS error during evaluation |
| timeout | 2 | 0.62% | the lookup did not complete in time |
How domains end their record — a distribution, not a defect rate
162 of 320 domains (50.62%) end their record in
~all. That is the largest single number here and it is the easiest one to
misread. ~all is a valid qualifier: RFC 7208 §8.5 asks receiving software
not to reject a message on that result alone. What this counts is what domain owners
chose, not how many of them are broken.
| Ending | Domains | Share of 320 |
|---|---|---|
~all softfail | 162 | 50.62% |
?all neutral | 1 | 0.31% |
+all pass-everything | 0 | 0.00% |
| no record at all | 35 | 10.94% |
The corpus does not record a -all count, so this page does not
state one. Subtracting the rows above from the 285 domains that have a record
would give a number nobody measured, and would assume these categories are mutually
exclusive and exhaustive. They may be; the file does not say so.
Findings, per domain
Counted per domain, not per occurrence: a record with three duplicate includes contributes one.
| Registry key | Domains | Share | Severity |
|---|---|---|---|
| spf-softfail-vs-fail | 162 | 50.62% | warn |
| spf-no-record | 35 | 10.94% | fail |
| spf-nested-include-counted-twice | 15 | 4.69% | warn |
| spf-too-many-dns-lookups | 13 | 4.06% | fail |
| spf-multiple-records | 10 | 3.12% | fail |
| spf-record-differs-across-resolvers | 7 | 2.19% | warn |
| spf-record-syntax-and-encoding | 7 | 2.19% | fail |
| spf-include-lookup-failed | 6 | 1.88% | fail |
| spf-include-has-no-spf | 4 | 1.25% | fail |
| spf-neutral-all | 1 | 0.31% | warn |
| spf-ptr-mechanism | 1 | 0.31% | warn |
| spf-plus-all | 0 | 0.00% | fail |
| spf-void-lookups | 0 | 0.00% | fail |
Rules that do not exist yet
Five of the eighteen registry keys have no rule implemented. They are absent from the counts above, not zero — a rule nobody wrote cannot find anything, and rendering it as 0% would claim no domain has the problem.
| Registry key | Domains | Share | Severity |
|---|---|---|---|
| spf-a-mechanism-behind-cdn-proxy | not measured | not measured | warn |
| spf-flattening-risks | not measured | not measured | info |
| spf-ip-range-too-broad | not measured | not measured | warn |
| spf-mx-mechanism-cost | not measured | not measured | info |
| spf-return-path-domain-mismatch | not measured | not measured | warn |
What this page deliberately does not show
The corpus carries five example domains for each finding. They are real third parties who ran the public audit, and naming them here as having a broken record is not something they agreed to. The examples stay in the corpus, where they serve reproduction, and off this page.
Reviewed by Venkat Nookala, last reviewed 2026-09-08.