SPF (Sender Policy Framework, RFC 7208) is a DNS TXT record that lists which mail servers may send email for a domain. A receiving server takes the IP address that connected to it, looks up the SPF record of the envelope sender's domain, and gets a result such as pass, fail or softfail.
The domain it checks is the envelope sender, also called the Return-Path or MAIL FROM. That is often not the address in the From line that people read. A mailing platform may use its own bounce domain, so SPF passes for the platform and says nothing about your brand.
Anatomy of a real record
Google's record, read with dig +short TXT google.com on 19 October 2026:
v=spf1 include:_spf.google.com ~all
Microsoft's, read the same day, uses five includes and a hard fail:
v=spf1 include:_spf-a.microsoft.com include:_spf-b.microsoft.com include:_spf-c.microsoft.com include:_spf-ssg-a.msft.net include:_spf1-meo.microsoft.com -all
| Part | Example | Meaning |
|---|---|---|
| Version | v=spf1 |
Must open the record |
| Mechanism | include:_spf.google.com |
Also accept whatever that domain's SPF allows |
| Mechanism | ip4:192.30.252.0/22 |
Accept this address range (from GitHub's record) |
| Qualifier + all | ~all or -all |
What to say about every server not listed |
The four qualifiers are + pass (the default), - fail, ~ softfail and ? neutral. A record ending in +all authorises the whole internet and is worse than no record.
The 10-lookup limit
Each include, a, mx, ptr, exists and redirect costs one DNS lookup, and so do the includes nested inside them. RFC 7208 section 4.6.4 caps the total at 10. Go over it and receivers return permerror, which DMARC counts as no SPF pass. ip4 and ip6 cost nothing, which is why GitHub's long record lists its own ranges directly.
There is a second, quieter limit. Lookups that return nothing, such as an include for a provider that deleted its SPF zone, are "void lookups", and the RFC says receivers should allow no more than two. Old includes for tools you cancelled years ago are the usual cause.
How it fits with DKIM and DMARC
SPF answers "was this server allowed?" DKIM answers "was this message signed, and is it unchanged?" Forwarding breaks SPF, because the forwarder's IP is not in your record, but usually leaves DKIM intact. DMARC uses either result, as long as the domain that passed aligns with the From domain.
In our scan of the Tranco top 1,000 domains on 19 October 2026, 430 records ended in -all, 266 in ~all, and 247 domains had no SPF record.
Three common mistakes
- Two SPF records. Adding a new provider as a second
v=spf1TXT record instead of editing the first. Section 4.5 of the RFC turns that into permerror for every message. - Too many includes. When we checked on 19 October 2026,
_spf.google.comandspf.protection.outlook.comcost one lookup each, while_spf.salesforce.comcosts two because it runs anexistscheck, andsendgrid.netcosts two because it includesab.sendgrid.net. Your ownaandmxmechanisms count too. Ten goes faster than it looks. Cut dead senders first; flatten last. - Expecting SPF alone to stop spoofing. It doesn't. A spoofer sets the bounce address to a domain they control, passes SPF there, and puts your name in From.
Next step
Count your lookups with the free SPF checker. For every mechanism, macros, flattening and provider includes, read the full SPF guide.