What is SPF? A Beginner's Guide to Sender Policy Framework
If you only learn one piece of email authentication first, make it SPF. It is the oldest of the three pillars, the simplest to explain, and — despite all of that — the one most likely to contain a quiet error that has been costing you delivery for months.
The one-line summary
Sender Policy Framework, defined in RFC 7208, is a public list. The list lives in DNS, it is owned by the operator of a domain, and it answers a single question: which IP addresses are permitted to put mail into the queue claiming to come from this domain?
The original problem SPF was designed to solve is straightforward. The SMTP protocol gives a sender free choice of envelope address. Anyone with a mail client can connect to a receiving server and announce MAIL FROM:<ceo@your-bank.com> — the protocol has no built-in way to challenge that claim. SPF removes the abuse by binding the right to use a domain to a specific set of IPs.
The check, step by step
An SPF evaluation runs at the very start of an SMTP conversation, before the message body is transferred:
- A sending server opens a TCP connection to the receiving server on port 25.
- It sends
MAIL FROM:<alice@example.com>. - The receiver takes the domain part —
example.com— and queries DNS for any TXT records starting withv=spf1. - It walks the mechanisms in that record left-to-right against the IP it is currently talking to.
- The first mechanism that matches wins, and its qualifier (pass, fail, softfail or neutral) becomes the result.
Whatever the outcome — pass, fail, softfail, neutral, none, temperror or permerror — it ends up stamped into the message's Authentication-Results header for downstream filters to use.
What the record looks like
An SPF record is a plain DNS TXT record on the domain you want to authorise. A typical example:
example.com. IN TXT "v=spf1 ip4:198.51.100.20 include:_spf.google.com -all"
Read left-to-right:
v=spf1— the version tag. Mandatory and always literally this string.ip4:198.51.100.20— the literal IPv4 address of one of your sending hosts is approved.include:_spf.google.com— pull in the SPF record published by Google so Workspace mail passes too.-all— if nothing earlier matched, treat the message as a hard fail.
The mechanisms you will actually use
ip4:andip6:— literal addresses or CIDR ranges.aora:somedomain— the A records of a domain are authorised.mxormx:somedomain— the MX hosts are authorised. Useful if your inbound and outbound traffic share the same servers.include:somedomain— recursively pull in another domain's SPF policy. This is how SaaS senders (Mailchimp, Microsoft 365, SendGrid) get authorised.exists:andptr— rare in production.ptris officially deprecated and you should not use it.
Qualifiers and what they actually mean
+(pass) — the implicit default. Almost never written out.-(fail) — reject this connection.~(softfail) — suspicious but accept anyway, usually to spam.?(neutral) — no opinion. Indistinguishable from no SPF at all.
The qualifier on the trailing all is the most important character in the entire record. -all says "anyone not listed is unauthorised, drop it." ~all says "treat anyone not listed as suspicious." Once your record is complete and you trust it, you should be using -all.
The 10-DNS-lookup ceiling
This is the trap that catches more senders than anything else. RFC 7208 caps the number of DNS lookups an SPF evaluation may trigger at ten. Every include, a, mx, exists and redirect contributes one — and includes are recursive, so a single nested include chain can blow the budget on its own. Cross the line and the receiver returns permerror; the connection's IP is irrelevant at that point.
It is easy to hit. Layer Google Workspace, Microsoft 365, Mailchimp, Zendesk and your own outbound platform on top of each other and you can be over twenty lookups before you realise.
The remedies are usually one of:
- Flatten the includes into raw
ip4:/ip6:mechanisms using a tool that resolves them for you (and re-runs daily, since the underlying IPs drift). - Drop senders you no longer use.
- Use an SPF macro service if you genuinely cannot fit otherwise.
The mistakes we see most often
- Two SPF records on the same domain. Only one
v=spf1record is permitted. A second one means automaticpermerror. Merge them. - No
allat the end. Without it, the policy quietly defaults to neutral and gives you no protection. - Using
?allin production. Equivalent to publishing nothing. If you publish SPF, mean it. - Dangerously wide records.
v=spf1 +alltells the world that any IP on the internet is permitted to send for you. This happens more often than you would think. - Forgetting a SaaS sender. If your CRM, billing system or scheduling tool sends mail under your domain, its IPs (or its include) must be in the record.
- Confusing the envelope-from with the visible From. SPF authenticates the envelope-from (Return-Path) only, not the From header your users actually see. Closing that gap is exactly why DMARC was invented.
Verifying your record
The fastest free check is the SPF lookup at MXToolbox. Drop in your domain and you get the parsed record, the resolved lookup count, and a list of any syntactic problems. From the command line:
dig +short TXT example.com | grep spf1
To prove that a specific IP actually passes, send a test message to a Gmail or Outlook account and inspect the original. The Authentication-Results header should read something like spf=pass smtp.mailfrom=example.com.
SPF is necessary but not sufficient
SPF was a useful first step in 2003 but its limitations are well known: it is broken by traditional forwarding, it does not authenticate the visible From header, and a passing SPF result tells you nothing about whether the message body was tampered with in transit. That is exactly why a modern setup combines SPF with DKIM and ties them together with a DMARC policy. Treat SPF as the foundation slab, not the entire house.