22 June 2026 · 6 min read
RFC 7208 is the standard that defines SPF (Sender Policy Framework). It was published in April 2014 and replaced RFC 4408. In short: a domain publishes one TXT record starting v=spf1; a receiver evaluates its terms left to right against the connecting IP address; the first matching term decides the result; and the result is one of seven values. Two limits break more real records than anything else: ten DNS-querying terms, and two lookups that return nothing.
What SPF checks
SPF checks whether the IP address delivering a message is allowed to send for the domain in the envelope sender, the address in the SMTP MAIL FROM command, which later appears as Return-Path:. RFC 7208 also recommends checking the HELO name the server gave. When the MAIL FROM is empty, as it is for bounce messages, SPF uses postmaster@ plus the HELO name instead.
SPF never looks at the From: header your recipient sees. Linking SPF to that address is DMARC's job, through alignment. Header From versus envelope From explains the difference.
The record
v=spf1 include:_spf.google.com include:sendgrid.net ~all- The record is a TXT record. RFC 7208 retired the separate SPF record type.
- It must start with
v=spf1. - A domain may publish one SPF record. If a receiver finds more than one, the result is
permerror. - Terms are separated by spaces and evaluated left to right until one matches.
- A record longer than 255 characters is published as several strings in one TXT record, which receivers join with nothing added between them, so put any needed space inside a string.
Terms are either mechanisms, which can match the connecting IP, or modifiers, which set parameters and never match.
Qualifiers
Each mechanism can start with a qualifier that sets the result when it matches:
| Qualifier | Result | Meaning |
|---|---|---|
+ (the default) | pass | Authorised |
- | fail | Explicitly not authorised |
~ | softfail | Probably not authorised, but do not reject on SPF alone |
? | neutral | The domain makes no assertion |
So ~all is the all mechanism with the softfail qualifier.
Mechanisms
| Mechanism | Matches when | DNS lookup? |
|---|---|---|
all | Always. It ends the record and sets the result for everything not matched earlier | No |
ip4: / ip6: | The IP is in the address or range. Without a prefix length, a single address (/32 or /128) | No |
a | The IP is one of the domain's A or AAAA addresses | Yes |
mx | The IP is one of the addresses of the domain's MX hosts | Yes |
include: | The named domain's SPF record returns pass | Yes |
exists: | The named domain has any A record; usually built with macros | Yes |
ptr | Reverse DNS of the IP falls under the domain. RFC 7208 says it SHOULD NOT be published | Yes |
Three behaviours worth knowing:
- Anything after
allis never evaluated, becauseallalways matches.~all include:vendor.examplenever reaches the include. include:only matches on a pass. If the included record returns fail, softfail or neutral, the include simply does not match and evaluation carries on.+allauthorises the whole internet. Never publish it.
Modifiers
redirect=domainreplaces the current record with another domain's record, but only when nothing in the current record matched. It is ignored if the record containsall, so use one or the other.exp=domainpoints to an explanation string a receiver may show when the result is fail.
Unknown modifiers are ignored rather than treated as errors.
The seven results
| Result | Meaning |
|---|---|
| none | No SPF record was found, or the domain does not exist |
| neutral | A ? term matched. RFC 7208 says receivers must treat it exactly like none |
| pass | The IP is authorised |
| fail | A - term matched, usually -all |
| softfail | A ~ term matched, usually ~all |
| temperror | A temporary problem, usually a DNS timeout. The receiver may try again later |
| permerror | The record cannot be evaluated: two SPF records, a syntax error or a limit exceeded |
permerror is always the domain owner's to fix. Repeated temperror usually means DNS problems at your provider or at a domain you include. Neither none nor neutral counts as a pass for DMARC.
The limits that break real records
Ten DNS-querying terms. During one evaluation, include, a, mx, ptr, exists and redirect together must not exceed ten, counting everything nested inside includes. One more returns permerror. ip4, ip6 and all cost nothing.
Nesting is what catches people out. Some providers flatten their own records to save you lookups; at the time of writing, _spf.google.com lists its addresses directly, while Mimecast's global include nests eight regional includes. Check your real total with the SPF checker.
Address lookups inside mx. Each MX host may resolve to at most ten addresses. Beyond that, the mx mechanism returns permerror. For ptr, names beyond the first ten are ignored.
Two void lookups. RFC 7208 says evaluation SHOULD stop with permerror after more than two lookups that return no answer (the name does not exist, or exists with no records). The usual cause is an include: pointing at a provider you stopped using whose record has been removed.
The standard fix for the ten-lookup limit is to replace includes with the addresses they resolve to, known as flattening. Flattened lists go stale when a provider changes its addresses, so they need re-checking; hosted SPF flattens your record and re-resolves it automatically.
Macros
RFC 7208 includes a macro language, written %{letter}, used mainly inside exists:. For example, exists:%{ir}.%{v}._spf.example.com builds a lookup name from the connecting IP. Most domains never need macros; SPF macros explained covers the syntax and the risks.
Walking a record like a receiver
For a connection from 209.85.220.41 with MAIL FROM:<alerts@example.com> and this record:
v=spf1 ip4:198.51.100.0/24 include:_spf.google.com include:sendgrid.net -allip4:198.51.100.0/24does not contain the address. No lookup used.include:_spf.google.comis fetched and evaluated. Google's record lists209.85.128.0/17, so it returns pass and the include matches. Result: pass.include:sendgrid.netand-allare never reached.
Because evaluation stops at the first match, a message from an early sender may never reach the later lookups. Do not rely on that: a sender further down, or a spoofed message that matches nothing, walks the whole record, so keep the full total within ten.
Where SPF stops
SPF answers one question about the envelope sender and returns one of seven results. It says nothing about the visible From address and nothing about what a receiver should do with a fail. DKIM signs the message, and DMARC ties SPF and DKIM to the From address and tells receivers what to do; DMARC versus SPF versus DKIM compares the three.
Run your domain through the SPF checker to see your current record, its lookup count and any permerror conditions.