SPF (RFC 7208) is a DNS record that lists the servers allowed to send email on your domain's behalf. It's the simplest of the three email-auth standards and also the most often misconfigured — usually because someone ran past the 10-lookup limit without realising it exists.
What SPF actually checks
When a receiver gets a message claiming to be from marketing@acme.com, it pulls the envelope sender (MAIL FROM) and looks up the SPF TXT record at that domain. Then it asks: does this record authorise the IP address that just connected to me?
SPF answers in one of five ways: pass, fail, softfail, neutral, or none (no record). It does not say what the receiver should do about a fail. That's DMARC's job.
Anatomy of a record
acme.com. IN TXT "v=spf1 mx include:_spf.google.com
include:servers.mcsv.net
ip4:198.51.100.0/24
-all"Mechanism by mechanism:
v=spf1— this is an SPF record. Required and exact.mx— authorise the servers in this domain's MX records. (Costs 1 DNS lookup.)include:_spf.google.com— ask Google's SPF record what IPs to authorise. (Costs 1 lookup, plus any includes Google uses recursively, which all count toward your 10-lookup budget.)ip4:198.51.100.0/24— authorise these literal IPs. (No DNS lookup.)-all— the terminator. Anything not authorised by the mechanisms above hard-fails. The alternatives are~all(soft-fail),?all(neutral), and+all(which means “everyone in the world is authorised to send mail as me” — this is never what you want).
The 10-lookup cliff
RFC 7208 caps SPF resolution at 10 DNS lookups. Every include:, a:, mx:,exists:, ptr:, and redirect= mechanism counts. If your record exceeds 10, the receiver evaluates SPF as permerror, which fails DMARC alignment on SPF without giving you any obvious error.
This is the single most common SPF bug we see in production. Operators add include:_spf.google.com, include:servers.mcsv.net, include:spf.protection.outlook.com, include:_spf.salesforce.com, include:sendgrid.net — and that's 5 lookups before counting recursion. Each of those records often contains its own includes. Salesforce alone resolves to about 7 lookups. You're past 10 before you notice.
The cliff is silent. There is no error in your sent mail. There is no bounce. Receivers just stop counting SPF as a vote in your favour. Your DMARC reports show messages failing alignment on SPF; you scratch your head; you never look at the lookup count because no tool surfaces it. We built one: SPF lookup counter. It walks the tree and tells you exactly which include pushed you over.
Flattening — what it is, when to do it
When you can't reduce includes (every ESP genuinely needs to be authorised), the alternative is flattening: replacing include:vendor.com with the actual ip4: ranges that vendor publishes. That collapses a 7-lookup include into 0 lookups (just literal IPs).
The trade-off:
- Pros: drops you back under 10 lookups, instantly. Resolves identically to the unflattened version.
- Cons: the IPs you froze can change. If Mailchimp adds a new sending range, your flattened SPF doesn't know about it; mail from those new IPs starts failing alignment. You need to re-flatten weekly or biweekly.
Doing this manually is tedious. Mailstinger's hosted SPF auto-flattens when you exceed the cap and re-resolves the includes nightly so the published IPs stay current. The point isn't that flattening is magic; the point is that the routine maintenance shouldn't be your routine.
The terminator — the choice that actually matters
-all, ~all, ?all, +all. These mean different things and the difference is visible in your spam folder.
-all(hard fail) — the receiver must reject or quarantine unauthorised mail. The right answer for most domains. Use this.~all(soft fail) — the receiver should accept but treat as suspicious. Useful as a temporary state during initial SPF rollout while you discover senders. Move to-allwithin weeks, not months.?all(neutral) — “don't use SPF for a verdict.” Almost never useful. Effectively no SPF policy.+all(pass everything) — do not do this. Authorises every IP on the internet to send mail as you. We have seen this in production at large companies, presumably because someone copy-pasted a stale example. Audit and remove.
What changed in 2024
Google and Yahoo's February 2024 sender requirements tightened the SPF math. For bulk senders (anyone over ~5,000 messages a day to those providers), SPF must pass for DMARC alignment — not just exist. Apermerror from being over the lookup limit is now a deliverability problem, not just a hygiene one.
The audit you should do today:
- Run the SPF lookup counter on your domain.
- If lookup count ≤ 8, you're fine; verify
-alland move on. - If lookup count is 9 or 10, you have one bad addition away from the cliff. Trim or flatten now.
- If > 10, you are silently failing SPF alignment for some mail right now. Flatten today.
Things people get wrong
- “I'll add SPF for marketing.” SPF is per envelope-domain, not per send-flow. You authorise servers, not message types.
- “Multiple TXT records is fine.” Two SPF records at the same name = both invalid. Receivers return
permerror. Combine into one. - “ptr: is fine.”
ptrcosts an unbounded number of lookups and is slow. RFC 7208 §5.5 explicitly recommends against it. We recommend more strongly: never. - “A subdomain inherits the parent's SPF.” It does not. Each subdomain that sends mail needs its own SPF record. (DMARC inherits via
sp; SPF doesn't.)