SPF record syntax
Published 17 September 2026
Every mechanism and modifier, with the column other guides leave out: what each one costs. An SPF record is allowed ten DNS lookups to evaluate, and going over does not produce an error you will see: it makes the record invalid and your mail starts failing while it still reads perfectly.
The shape of a record
v=spf1 ip4:192.0.2.0/24 include:_spf.google.com ~all
A single DNS TXT record at the domain root. It starts with
v=spf1, then a list of mechanisms describing
who may send, evaluated left to right, and ends with a catch-all saying what
to do with everyone else. The first mechanism that matches decides the
result; nothing after it is read.
One record per domain. Publishing two is a permanent error: receivers do not merge them, they give up.
Mechanisms, and what they cost
| Mechanism | Matches | DNS lookups |
|---|---|---|
| ip4:192.0.2.0/24 | An IPv4 address or range | 0 |
| ip6:2001:db8::/32 | An IPv6 address or range | 0 |
| include:example.com | Whatever that domain's SPF allows | 1 + its chain |
| a / a:example.com | The domain's own A/AAAA records | 1 |
| mx / mx:example.com | The domain's mail servers | 1 |
| exists:%{i}.example.com | Whether a constructed name resolves | 1 |
| ptr | Reverse DNS of the sender. Deprecated. | 1 |
| all | Everything. Always last. | 0 |
ip4 and ip6 are free. Everything else costs, and
include costs whatever the included record costs as well, which
is where budgets disappear without anyone editing anything.
Qualifiers
Any mechanism can be prefixed. Omit it and you get +.
+Pass: allowed. The default.-Fail: not allowed, reject it.~SoftFail: not allowed, but accept and mark it.?Neutral: no opinion, treated as no policy at all.
In practice qualifiers matter on all and almost nowhere else.
Ending the record: ~all or -all
~all asks receivers to accept unlisted senders and mark them.
-all asks them to reject. Start with ~all while
you are still discovering which services send on your behalf, including
invoicing, ticketing and the marketing platform somebody signed up for. Tighten to
-all once a few weeks of DMARC reports show nothing legitimate
failing.
+all authorises the entire internet. It appears
in the wild surprisingly often, usually from someone debugging a delivery
problem who never took it out. It is worse than publishing no SPF at all,
because it looks deliberate.
Modifiers
redirect=example.com: use that domain's record instead of this one. Costs a lookup. Ignored if the record contains anall, which is a common source of confusion: if you have both, theredirectnever runs.exp=explain.example.com: a domain holding a human-readable rejection message. Rarely used and costs nothing unless a failure occurs.
The ten-lookup limit
This is the part worth reading twice, because it is how working SPF setups break without being touched.
Evaluating a record may cost at most ten DNS lookups, and that budget spans the whole chain: your includes, their includes, and so on. Add one provider to a record already at nine and you cross the line.
Past ten, the result is PermError. Receivers treat a PermError
as though the domain published no SPF at all. Your record is still
there, still reads correctly, still shows as "found" by most checkers, and
is doing nothing. Mail that passed last week now fails, and nothing in the
record changed. Somebody else's include grew.
Common culprits, with typical costs:
| Include | Roughly costs |
|---|---|
| include:_spf.google.com | 3–4 |
| include:spf.protection.outlook.com | 1–2 |
| include:sendgrid.net | 2 |
| include:_spf.salesforce.com | 2 |
| include:servers.mcsv.net | 1 |
Four providers and you are over half the budget. These numbers change without notice, which is the point: the cost is not yours to control.
Count yours. The SPF, DKIM and DMARC checker walks the full include chain and shows what each step costs, so you can see which include spent the budget rather than only that it was spent. It runs in your browser and sends nothing anywhere.
Getting back under the limit
- Remove what you no longer use. Almost every over-limit record contains a provider nobody has sent through in two years. This is the first thing to check and usually the whole fix.
- Flatten an include into ip4 entries. Costs nothing to evaluate, but you now own keeping those addresses current, and when the provider renumbers, your mail breaks. A trade, not a free win.
- Use a subdomain for bulk mail. Send marketing from
mail.example.comwith its own record and its own budget. The cleanest fix, and it isolates your reputation as well.
Mistakes worth knowing
- Two SPF records. A permanent error. Merge them.
- Publishing on a subdomain and expecting inheritance.
SPF does not inherit.
mail.example.comneeds its own record. - A record over 255 characters in one string. TXT values are limited to 255-byte chunks; longer records must be split into adjacent quoted strings, which receivers rejoin. Most DNS hosts handle this, some do not.
- Using
ptr. Deprecated, slow, and ignored or penalised by many receivers. Replace it. - SPF alone. SPF validates the envelope sender, not the From address a recipient actually sees. Without DMARC tying them together, a spoofer can pass your SPF while displaying your address.