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

MechanismWhat it matchesDNS cost
ip4:192.0.2.0/24An IPv4 address or CIDR range0 lookups
ip6:2001:db8::/32An IPv6 address or CIDR range0 lookups
a / a:domain.comThe A record(s) of the domain1 lookup
mx / mx:domain.comThe MX records of the domain (and their A records)1+ lookups
include:other.comRecursively evaluates other.com's SPF record1 lookup, plus everything inside
exists:domain.comTrue if the domain resolves (used in macro tricks)1 lookup
allAlways 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 FROM or Return-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 of include:, their IP change breaks you silently.
  • Subdomain has no record — SPF does not inherit from parent domain. mail.example.com needs 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

  1. 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.
  2. Confirm the record exists and parses. Run dig +short TXT example.com. There should be exactly one line starting with v=spf1.
  3. Count lookups. Paste your record into an SPF validator (MXToolbox SPF Record Lookup, dmarcian SPF Surveyor, EasyDMARC). The tool counts lookups and flags permerror.
  4. 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 an include:.
  5. 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