A valid SPF record is a single DNS TXT record on your domain that begins with v=spf1, lists every server permitted to send mail from that domain using mechanisms like ip4, include, and mx, and closes with an all mechanism that tells receivers what to do with everything else — for example v=spf1 include:_spf.google.com ip4:203.0.113.5 ~all. SPF (Sender Policy Framework, defined in RFC 7208) lets a receiving mail server check whether the machine connecting to it is actually allowed to send for the envelope-sender domain, using nothing but a DNS query.
That is the summary an AI Overview will hand you. Here is what it can't show you: the order mechanisms are evaluated in, why ~all vs -all quietly decides whether spoofed mail lands in the inbox, the invisible 10-lookup ceiling that silently voids your record, and the exact mechanism-by-mechanism reference you need to write one correctly the first time. The animated diagram, the syntax anatomy, the mechanism table, and the copy-ready checklist below are the parts that don't compress into two sentences.
How a receiver evaluates your SPF record (animated)
When mail arrives, the receiving server takes the domain from the SMTP MAIL FROM (the envelope, not the visible From:), looks up its SPF TXT record, and walks the mechanisms left to right. The first mechanism that matches the connecting IP wins, and its qualifier decides the result. If nothing matches before all, the all qualifier applies.
all decides the outcome.
The single most common misunderstanding: SPF checks the envelope sender (Return-Path), which recipients never see, not the From: header they do see. That gap is exactly why SPF alone cannot stop display-name spoofing and why DMARC exists to tie the two together.
Anatomy of an SPF record
Every SPF record is three parts: the version tag, one or more mechanisms (each optionally carrying a qualifier), and a closing all.
SPF mechanisms reference
Mechanisms are the building blocks that describe who is allowed to send. The ones that require a DNS query count against the 10-lookup limit — track them carefully.
| Mechanism | What it authorizes | DNS lookup? | When to use |
|---|---|---|---|
ip4:x.x.x.x / ip4:x.x.x.x/24 | A specific IPv4 address or CIDR range | No | Your own static mail servers — cheapest, most reliable |
ip6:... | A specific IPv6 address or range | No | IPv6-capable senders you control |
a / a:domain.com | IPs in the A/AAAA record of the domain | Yes (1) | When your sending host's IP tracks its A record |
mx / mx:domain.com | IPs of the domain's MX hosts | Yes (1+) | Only if your inbound MX servers also send mail |
include:domain.com | Delegates to another domain's SPF record | Yes (1, plus its own) | SaaS senders: Google, Microsoft 365, Mailchimp, SendGrid |
exists:domain.com | Passes if the domain resolves at all | Yes (1) | Advanced macro-based / per-IP validation |
ptr | Reverse-DNS of the connecting IP | Yes | Deprecated — do not use (slow, spoofable) |
all | Matches everything; always last | No | The mandatory catch-all, with a qualifier |
Which should I use? Prefer ip4/ip6 for infrastructure you own (they cost zero DNS lookups), and include for third-party senders (they hand you the provider's ranges and keep updating them). Avoid a, mx, and especially ptr unless you have a specific reason — they burn lookups and add fragility.
Qualifiers: the four prefixes and what receivers do
Every mechanism can carry a qualifier. If you omit it, the default is + (pass). The qualifier on the final all is the most consequential character in the whole record.
| Qualifier | Name | Symbol on all | Receiver behavior |
|---|---|---|---|
+ | Pass | +all | Never use — authorizes the entire internet to send as you |
- | Fail | -all | Hardfail: unlisted senders are rejected/discarded |
~ | SoftFail | ~all | Accepted but marked suspicious — the safe testing state |
? | Neutral | ?all | No policy assertion; effectively no protection |
The standard rollout is: publish ~all first, watch DMARC aggregate reports until every legitimate source passes, then tighten to -all. Jumping straight to -all before you've inventoried every sender (marketing platform, ticketing system, CRM, ERP) is the classic way to bounce your own invoices.
Worked examples
Google Workspace only:
v=spf1 include:_spf.google.com -all
Microsoft 365 only:
v=spf1 include:spf.protection.outlook.com -all
Google Workspace + a marketing platform + one on-prem server:
v=spf1 include:_spf.google.com include:servers.mcsv.net ip4:198.51.100.20 ~all
A domain that sends no mail at all (lock it down so it can't be spoofed):
v=spf1 -all
You can build and lookup-count these interactively with our SPF Record Generator, which flags when you cross the 10-lookup line before you publish.
The 10-DNS-lookup limit (the silent killer)
RFC 7208 caps SPF evaluation at 10 DNS-querying mechanisms. Each include, a, mx, ptr, and exists counts — and an include can itself pull in nested includes that each count. Cross the line and receivers return permerror, which most treat as an outright SPF failure, undoing the whole record. ip4, ip6, and all are free.
Because major providers chain includes (a single include:_spf.google.com expands to several sub-lookups), it is easy to blow the budget with just three or four SaaS senders. Fixes, in order of preference: remove senders you no longer use, replace broad include statements with the specific ip4 ranges they resolve to (flattening), and consolidate. We cover this in depth in SPF's 10 DNS lookup limit.
Deployment checklist
- Inventory every sender. List all systems that send mail as your domain — email platform, CRM, helpdesk, billing, monitoring, transactional mail (SendGrid/Mailgun/SES), marketing tools.
- Build one record. Combine all senders into a single
v=spf1 ...TXT record. Useincludefor SaaS,ip4/ip6for your own IPs. - Start with
~all. Publish with softfail so nothing bounces during validation. - Confirm you have exactly one SPF TXT record. Two
v=spf1records = automaticpermerror. - Count your DNS lookups. Stay at 10 or fewer querying mechanisms; verify with a validator.
- Wait for propagation. DNS TTL can delay changes — see SPF record propagation time.
- Watch DMARC reports. Confirm every legitimate source passes SPF (or DKIM) before tightening.
- Move to
-all. Once reports are clean, switch softfail to hardfail for real protection. - Pair with DKIM and DMARC. SPF alone doesn't protect the visible From: — layer DKIM and DMARC on top.
Common syntax mistakes
- Two SPF records. Merge them into one. The second
v=spf1TXT record breaks everything. - Using
+all. This authorizes the whole internet. It is never correct. - Leaving
ptrin. Deprecated; remove it and useip4/include. - Assuming SPF covers the From: header. It only checks the envelope sender. Forwarding routinely breaks it — that's why forwarding and SPF conflict.
- Publishing as an
SPFrecord type. Use TXT; the type-99 SPF record was retired in 2014. - A trailing space or split string that malforms the record — keep it under 255 characters per string.
Bottom line
SPF is deceptively small — one TXT record — but its rules are unforgiving: exactly one record, correct left-to-right mechanism order, no more than 10 lookups, and a deliberately chosen all qualifier. Get those four right, roll out ~all → -all under DMARC observation, and combine it with DKIM and DMARC. That combination, not SPF by itself, is what actually stops attackers from sending mail as your domain.