How SPF Works
Sender Policy Framework (SPF) is a DNS-based way for a domain owner to publish a list of IP addresses authorized to send mail on the domain's behalf. When a receiver gets a message, it looks up this list and checks whether the connecting server's IP is on it. SPF is simple in principle but has subtle limits — the 10-lookup cap, the difference between envelope and visible sender, and behavior under forwarding — that catch nearly every operator at some point.
What an SPF record looks like
An SPF record is a single DNS TXT record at the domain that needs to authorize senders. Example:
example.com. IN TXT "v=spf1 ip4:203.0.113.0/24 include:_spf.google.com include:sendgrid.net ~all"
Breaking that down:
v=spf1— required version tag. Must be the first element.ip4:203.0.113.0/24— authorizes any IP in this range to send for this domain.include:_spf.google.com— pulls in Google's published SPF record (used when you send via Gmail/Workspace).include:sendgrid.net— pulls in SendGrid's published SPF record.~all— soft fail policy. Any IP not matched above should be treated as suspicious but not rejected outright.
Mechanisms are evaluated left to right. The first match wins and determines the result.
The mechanisms you actually use
| Mechanism | What it matches | DNS cost |
|---|---|---|
ip4:192.0.2.0/24 | An IPv4 address or CIDR range | 0 lookups |
ip6:2001:db8::/32 | An IPv6 address or CIDR range | 0 lookups |
a / a:domain.com | The A record(s) of the domain | 1 lookup |
mx / mx:domain.com | The MX records of the domain (and their A records) | 1+ lookups |
include:other.com | Recursively evaluates other.com's SPF record | 1 lookup, plus everything inside |
exists:domain.com | True if the domain resolves (used in macro tricks) | 1 lookup |
all | Always matches. Used at the end as the catch-all rule. | 0 lookups |
The ptr mechanism exists but is deprecated by RFC 7208 — it is slow, often broken, and should not be used.
The result qualifiers
Each mechanism can be prefixed with a qualifier that determines what result a match produces:
+(pass) — the default. The IP is authorized.-(fail / hard fail) — the IP is not authorized. Receiver should reject.~(softfail) — the IP is probably not authorized. Receiver should accept but mark.?(neutral) — explicit "no opinion." Receiver should treat as if SPF was not present.
The qualifier on the final all mechanism is what almost everyone discusses when they ask "hard fail or soft fail." -all means "anything not matched above is unauthorized and should be rejected." ~all means "anything not matched above is probably unauthorized but accept it for now." Most domains start with ~all during rollout and move to -all after verifying their record is complete.
The 10-lookup limit
RFC 7208 caps the total DNS lookups during SPF evaluation at 10. Every include:, a, mx, ptr, and exists mechanism counts as one lookup. Lookups inside include:s count toward the same total — there is no recursion budget reset. Exceeding the limit returns permerror, which DMARC treats as a failure.
This limit is the single most common cause of SPF problems in growing organizations. A typical record that looks innocent —
v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org include:_spf.salesforce.com include:servers.mcsv.net include:spf.protection.outlook.com include:_spf.intuit.com ~all
— may exceed the limit even though it only has seven includes visible, because each of those includes pulls in its own nested includes, and they all count. Google's _spf.google.com is itself three lookups deep.
Fixes for hitting the limit
- Flatten the record. Tools like dmarcian, EasyDMARC, and PowerDMARC offer "SPF flattening" services that replace
include:s with the literal IP ranges they expand to. The flattened record needs zero lookups but must be re-flattened whenever any included service changes its IPs. - Drop services you no longer use. The most common bloat source is leftover
include:s from services you stopped using years ago. Audit your record and remove anything not actively sending. - Use SPF macros to conditionally authorize specific subdomains for specific services. This is advanced; reach for it only if flattening cannot fit your needs.
SPF authenticates the envelope sender, not the visible From:
This trips up nearly everyone the first time they encounter it. Email has two "From" addresses:
- The envelope sender (also called
MAIL FROMorReturn-Path) — used in the SMTP transaction. Receivers send bounces here. - The header From: — the address users see in their mail client.
SPF checks the envelope sender. The two can differ — a marketing platform often sets MAIL FROM: bounces@platform.com while the visible From: is news@yourbrand.com. SPF for platform.com passes (because platform.com publishes the IPs they send from), but a recipient who only looks at the visible From: would not know SPF was checked against a completely different domain. This is exactly why DMARC adds alignment — it requires the SPF-authenticated domain to align with the visible From: domain.
Common SPF failure causes
- Permerror from too many lookups — see above. Flatten or drop services.
- Two SPF records on one domain — RFC 7208 requires exactly one. Both are invalid. Combine them.
- A new service not yet added — onboarding a new email provider without updating SPF. Their IPs send mail; SPF rejects.
- Service rotated their IPs — if you used hardcoded
ip4:instead ofinclude:, their IP change breaks you silently. - Subdomain has no record — SPF does not inherit from parent domain.
mail.example.comneeds its own SPF record if mail is sent from that subdomain. - Forwarded mail — expected behavior. The forwarder's IP is not in your SPF. This is why DKIM also matters.
How to debug SPF
- View the failing message's headers. Look for
Authentication-Results:. It tells you the exact result and the domain checked. Example:spf=fail (sender IP is 198.51.100.5) smtp.mailfrom=bounces@example.com. - Confirm the record exists and parses. Run
dig +short TXT example.com. There should be exactly one line starting withv=spf1. - Count lookups. Paste your record into an SPF validator (MXToolbox SPF Record Lookup, dmarcian SPF Surveyor, EasyDMARC). The tool counts lookups and flags permerror.
- Check the sending IP. Get the IP from the failing message's headers. Verify it falls within one of the ranges your SPF authorizes — either directly (
ip4:) or via aninclude:. - If a third party is involved, check their documentation for the current SPF include they recommend. Some services change theirs over time.
Frequently Asked Questions
What is the SPF 10-lookup limit?
RFC 7208 limits an SPF record evaluation to a maximum of 10 DNS lookups. Every include:, a, mx, ptr, and exists mechanism counts as one lookup. If your record exceeds the limit, receivers return permerror — treated as a failure by DMARC. The limit exists to prevent SPF from being used as a DNS amplification vector. Most organizations exceed it by stacking too many third-party include:s — fix by flattening or using SPF macros.
Should I use -all or ~all in my SPF record?
Use -all (hard fail) once you have verified every legitimate sender is included. Hard fail tells receivers to reject unauthorized mail; soft fail (~all) only marks it suspicious. During initial rollout, ~all is safer because errors do not bounce mail. Once DMARC is at p=reject and aggregate reports show no missing senders, switch to -all for strongest protection.
Can I have multiple SPF records for one domain?
No. RFC 7208 explicitly requires exactly one SPF record per domain. Receivers seeing multiple SPF records return permerror — both records are treated as invalid. If you need to authorize multiple sending services, combine them into a single record with multiple include: mechanisms separated by spaces.
Does SPF survive email forwarding?
No. When a mailbox forwards your message to another address, the forwarding server connects from its own IP — which is not in your SPF record — so SPF fails. DKIM survives forwarding because the signature travels with the message body. This is why DMARC requires only one of SPF or DKIM to pass and align, not both — to tolerate forwarding scenarios.
How do I debug an SPF failure?
Three steps: (1) view the email's Authentication-Results header to see the exact failure reason; (2) use a CLI tool like dig +short TXT yourdomain.com to confirm the SPF record exists and is syntactically valid; (3) use an online SPF validator like MXToolbox or dmarcian to check lookup count and detect permerrors. Common failures are exceeding 10 lookups, having multiple SPF records, missing an include for a new third-party service, or the third party using an IP outside their published range.
Related Guides
More From This Section
All Email Guides
SPF, DKIM, DMARC, MX records, deliverability, and email headers.
DNS Records for Email
The complete DNS-record checklist for a mail-sending domain — MX, SPF, DKIM, DMARC, MTA-STS, TLS-RPT, BIMI, and reverse…
SPF, DKIM, DMARC: How Email Authentication Works
SPF, DKIM, and DMARC explained in plain English — how the three email authentication standards work together, what each…
Run a Speed Test
Measure download, upload, ping, and jitter in your browser.