What is SPF? A plain-English guide
SPF — Sender Policy Framework, defined in RFC 7208 — is a DNS record that lists which servers are allowed to send email for your domain. When a message arrives, the receiving server looks up the SPF record of the domain in the message's envelope sender and checks whether the connecting server's IP address is on the list. On the list: pass. Not on the list: the record's final term says what to do about it.
That is the whole mechanism. The details that matter in practice — which domain actually gets checked, why records break at 10 DNS lookups, and the forwarding blind spot that makes SPF insufficient on its own — are below.
What SPF actually checks
SPF does not check the From: address a person sees in their mail client. It checks the envelope sender — the MAIL FROM address given during the SMTP conversation, sometimes called the return-path or RFC5321.MailFrom. For bounce messages, which have an empty envelope sender, the receiver checks the sending server's HELO hostname instead.
The distinction matters because the two addresses are often different. When an email service provider sends a newsletter for you, the envelope sender is frequently a domain the ESP controls (a bounce domain like bounce.esp-example.com), while the visible From: is yours. SPF can pass on the ESP's domain while saying nothing about yours — which is exactly the gap DMARC alignment exists to close.
Reading an SPF record, term by term
An SPF record is a single TXT record at the root of the domain. A typical one looks like this:
v=spf1 include:_spf.google.com include:sendgrid.net ip4:203.0.113.25 ~all
Read left to right:
| Term | What it does |
|---|---|
v=spf1 | Version marker. Must be first; identifies the TXT record as SPF. |
include:_spf.google.com | Fetch that domain's SPF record and pass if its rules match. Used by nearly every hosted service. |
include:sendgrid.net | Same again for a second service. Every source you send through gets merged into this one record. |
ip4:203.0.113.25 | Authorize one literal IPv4 address (a /24-style range also works). ip6: is the IPv6 equivalent. |
~all | The catch-all: what receivers should do with everything not matched above. |
Other mechanisms you will meet: a (the domain's own A/AAAA addresses), mx (the addresses of its MX hosts), and exists: (a rarely-used custom lookup). The ptr mechanism also exists but RFC 7208 says not to use it — it is slow, unreliable, and some receivers skip it entirely. A redirect= modifier hands evaluation to another domain's record wholesale.
Evaluation stops at the first match, so order matters only for readability and for which term catches a given sender. Everything that matches nothing falls through to all.
~all vs -all: the qualifier at the end
Each mechanism can carry a qualifier: + (pass, the default), - (fail), ~ (softfail), ? (neutral). In practice the qualifier you choose for all is the one that matters:
-all(fail) — "anything not listed is not mine." The strongest statement. Receivers may refuse unlisted mail outright, even without DMARC.~all(softfail) — "anything not listed is probably not mine." Receivers accept the mail but treat the result as a failure signal. This is the common choice while you are still confirming you have listed every sender.?all(neutral) — says nothing at all; little reason to use it.+all— authorizes the entire internet to send as your domain. Never publish this.
Under DMARC the practical difference between ~all and -all shrinks: DMARC treats softfail and fail alike (both are "SPF did not pass"), and your DMARC policy decides the mail's fate. -all still matters for receivers that act on SPF alone, and it is the honest end state once your record is complete.
The 10-lookup limit and PermError
RFC 7208 caps SPF evaluation at 10 DNS lookups. Every include:, a, mx, exists:, ptr, and redirect= costs one lookup — and include: counts recursively: if a service's include fetches three more includes of its own, that is four lookups from your budget, not one. ip4:, ip6:, and all cost nothing.
Go over the limit and the result is PermError — a permanent evaluation error. This is the nastiest failure mode in SPF, because nothing looks wrong: the record still reads fine, publishing it raises no error anywhere, and mail keeps flowing. But receivers evaluating it hit the limit and stop, many treat PermError as a failure, and under DMARC a PermError means SPF can never produce an aligned pass for you. Your deliverability degrades with no error message anywhere you would see it.
The budget disappears faster than most people expect — three or four hosted services, each with nested includes, is often enough. Paste your domain or record into the SPF lookup counter to see the exact count and which mechanism is spending it.
Common failure modes
Multiple SPF records
A domain may publish exactly one SPF record. Two TXT records both starting v=spf1 — a classic result of "just add the record our service gave you" — is a PermError under RFC 7208, and both records stop working. The fix is always to merge: one v=spf1 line containing every source, one all at the end.
Too many includes
Every service's setup guide says "add our include." Nobody's setup guide says "check your lookup budget first." Records accrete includes over years — the ESP, the helpdesk, the CRM, the billing system, a service someone trialed in 2023 — until the eleventh lookup quietly breaks all of them. Audit before you add, and remove includes for services you no longer use.
Forgotten senders
The opposite failure: a legitimate sender that never made it into the record. The billing system that sends invoices, the monitoring tool that sends alerts, the office scanner that emails PDFs. Their mail softfails or fails SPF, and once DMARC enforcement is on, it starts disappearing. This is the main reason to monitor DMARC aggregate reports before tightening anything — the reports show you every source sending as your domain, including the ones you forgot.
SPF's blind spot: forwarding
When someone forwards mail — a university address that forwards to Gmail, an old mailbox that forwards to a new one — the forwarding server re-sends the message from its own IP address, but the envelope sender still carries your domain. The receiver checks your SPF record, does not find the forwarder's IP (of course not — you have never heard of it), and SPF fails on a message you legitimately sent.
Some forwarders work around this by rewriting the envelope sender (a scheme called SRS), which makes SPF pass but on the forwarder's domain — no longer aligned with yours. Either way, SPF alone cannot survive forwarding. This is not a configuration mistake you can fix; it is structural. DKIM is the mechanism that survives forwarding, because its signature travels inside the message rather than depending on the connecting IP.
SPF is one leg of a three-legged stool
SPF answers one narrow question: was this server allowed to send for the envelope domain? It says nothing about the From: address users see, and it breaks on forwarding. That is why the modern authentication stack is three records working together:
- SPF authorizes sending servers for the envelope domain.
- DKIM cryptographically signs the message itself.
- DMARC ties both to the visible
From:domain via alignment, and tells receivers what to do when neither checks out.
A pass that is not aligned with your From: domain does nothing for DMARC — which is why a technically valid SPF record can still leave a domain wide open to spoofing.
Where your record stands
The failure modes above — duplicate records, a blown lookup budget, missing senders, a permissive all — are all mechanically detectable. A scan of your domain checks the SPF record alongside DKIM, DMARC, MX, DNS, blacklists, and domain/SSL expiry, and shows exactly which mechanism is the problem when one is.
Check SPF, DMARC, DKIM, MX, DNS and expiry in one free scan — with the exact record to paste in to fix each problem.