"About eight, I think" is not an audit. This lesson is a procedure that produces a number you can show someone, with the working attached, so a disagreement about whether a record is safe can be settled rather than argued.
What spends from the budget
| Term | Cost | Note |
|---|---|---|
include | 1 + the entire cost of the target record | Recursive. This is where budgets disappear. |
a | 1 | With or without an argument. |
mx | 1 + one per MX host returned | The next lesson is entirely about this. |
ptr | 1 + more | Deprecated. Treat any occurrence as a finding. |
exists | 1 | Regardless of how many hosts it can authorise. |
redirect= | 1 + the cost of the target | Replaces rather than adds, but still spends. |
ip4, ip6, all | 0 | Free. Literal ranges never query anything. |
The initial fetch of the record itself is not counted (RFC 7208 §4.6.4), which is the most common off-by-one in a manual count.
The procedure
1. Fetch the record.
2. List every lookup-costing term, in order. Note the running total.
3. For each include or redirect target:
fetch it, repeat from step 2, add its total to yours.
4. For each mx: resolve the MX set, count the hosts, add one per host.
5. Stop the moment the running total exceeds 10 — that is a permerror
and nothing after it matters.
6. Record the tree, not just the number. The number without the tree
cannot be re-checked by anyone else.A worked audit
github.com, checked 2026-09-15. Top level first:
v=spf1 ip4:192.30.252.0/22
include:spf.protection.outlook.com 1
include:_netblocks.google.com 2
include:_netblocks2.google.com 3
include:mail.zendesk.com 4
include:_spf.salesforce.com 5
include:servers.mcsv.net 6
include:mktomail.com 7
include:sendgrid.net 8
ip4:... ip4:... ip4:... ip4:... free
~all freeNow open each include and add what it spends:
spf.protection.outlook.com +0 (ip4/ip6 literals only)
_netblocks.google.com +0
_netblocks2.google.com +0
mail.zendesk.com +0
_spf.salesforce.com +1 exists:%{i}._spf.mta.salesforce.com
servers.mcsv.net +0
mktomail.com +0
sendgrid.net +1 include:ab.sendgrid.net
---
TOTAL 10 of 10Ten of ten is not a passing grade
It means zero headroom. Any one of those eight platforms adding a single include to its own record — which they do, without telling anyone — pushes this domain to permerror and turns SPF off for it entirely. A record at 10 is not healthy; it is one vendor change from broken, and nobody will be notified.
Keep the tree
- The total alone is unfalsifiable. The tree lets a colleague re-run your count and disagree with a specific line.
- It shows where the budget went, which is what the remediation conversation needs. "Eight of our ten are third-party platforms" is an argument; "we are at ten" is not.
- Re-run it on a schedule. The count changes without you editing anything — module 8.