What Is SPF? Sender Policy Framework and the Envelope Sender
SPF (Sender Policy Framework) is a DNS TXT record in which a domain lists the hosts authorized to send mail using its name. The receiving server checks the connecting IP address against the record for the domain in the SMTP envelope: the MAIL FROM (Return-Path) and, as the standard recommends, the HELO name. It does not check the From address a reader sees. Most SPF confusion starts there.
SPF is one of three authentication layers covered in the SPF, DKIM and DMARC overview. The current specification is RFC 7208, published by the IETF in April 2014. It is no longer optional at the largest mailbox provider: Google states that all senders to personal Gmail accounts must set up SPF or DKIM, and that bulk senders, defined as more than 5,000 messages a day, must set up SPF, DKIM and DMARC.
What does SPF actually check?
The envelope sender, not the header sender. An email carries two sender addresses. The RFC5321.MailFrom is exchanged during the SMTP conversation and later surfaces as the Return-Path. The RFC5322.From is the header shown in the mail client. SPF reads the first one. M3AAWG's SPF best-practice document puts it without qualification: "When evaluating SPF, receivers use the domain taken from the Return-Path address (technically known as the RFC5321.MailFrom address). Other domains found in the message are not relevant for SPF."
The DMARC vendor dmarcian names the mistake directly: "a common misunderstanding is that SPF checks the "From:" domain of an email instead of the domain of the operator, known as the reverse-path."
RFC 7208 defines a second identity as well. "It is RECOMMENDED that SPF verifiers not only check the "MAIL FROM" identity but also separately check the "HELO" identity". Verifiers must check MAIL FROM if a HELO check was not performed or did not reach a definitive result. Bounces, which carry an empty reverse-path, are handled by substitution: the MAIL FROM identity becomes postmaster at the HELO domain.
Where does DMARC alignment come in?
SPF on its own says nothing about the visible From. That is the job of DMARC, now specified in RFC 9989 (May 2026), which obsoletes RFC 7489. RFC 9989 states that a DMARC pass "requires not only an SPF or DKIM pass verdict for the email message but also and more importantly that the domain associated with the SPF or DKIM pass be "aligned" with the Author Domain". Domains are in relaxed alignment when they share the same Organizational Domain, and in strict alignment only when they are identical.
RFC 7489, now replaced by RFC 9989, gave a worked example that still illustrates the two modes: a MAIL FROM domain of cbg.bounces.example.com aligns with a From address of payments@example.com in relaxed mode, but not in strict mode. For SPF specifically, dmarc.org's summary of the DMARCbis changes states that DMARC's SPF check uses the MAIL FROM identity only, with no fallback to HELO.
It follows from those rules that a message can pass SPF and still fail DMARC alignment, when the MAIL FROM belongs to an email service provider rather than to the sender's own domain. Amazon SES is a documented case. Its default MAIL FROM is a subdomain of amazonses.com, so SPF passes against Amazon's domain. To authenticate against a domain you own, SES requires a custom MAIL FROM subdomain with its own SPF record (and an MX record for bounces).
What does an SPF record look like?
A single DNS TXT record whose content begins with exactly v=spf1. RFC 7208 is strict on both points. "SPF records MUST be published as a DNS TXT (type 16) Resource Record (RR) [RFC1035] only." The dedicated SPF record type (99) from the experimental phase was removed in RFC 7208. Records that do not begin with exactly v=spf1 are discarded, so v=spf10 does not count.
After the version tag come terms, evaluated left to right. The first one that matches decides the result.
- Mechanisms:
all,include,a,mx,ptr,ip4,ip6,exists. - Modifiers:
redirectandexp. Unknown modifiers are ignored. - Qualifiers, prefixed to a mechanism:
+pass,-fail,~softfail,?neutral. "The qualifier is optional and defaults to "+"."
Names are case-insensitive. The mx mechanism authorizes the hosts named in the domain's MX records. The ptr mechanism is deprecated in the specification itself: "This mechanism SHOULD NOT be published."
A worked example
Google publishes combined records for Workspace domains that also send through a third party. For a domain that sends through Google Workspace and Zendesk, the record takes this form:
v=spf1 include:_spf.google.com include:mail.zendesk.com ~allA receiver evaluates it in order. include:_spf.google.com runs Google's own SPF record against the connecting IP. If that evaluates to a pass, the include matches and processing stops with a pass. If not, the receiver moves to include:mail.zendesk.com and does the same. If neither matches, ~all matches everything that is left and returns softfail.
The include mechanism behaves differently from its name, and the RFC concedes as much: "In hindsight, the name "include" was poorly chosen. Only the evaluated result of the referenced SPF record is used, rather than literally including the mechanisms of the referenced record in the first." A -all inside Google's record does not end evaluation of yours.
all belongs at the end. "Mechanisms listed after "all" MUST be ignored", and a redirect is ignored when all is present. A record with no all and no redirect that matches nothing returns neutral, "just as if "?all" were specified as the last directive."
What results can an SPF check return?
Seven. RFC 7208 defines none (no SPF record found), neutral, pass, fail, softfail, temperror and permerror. The two error results point in different directions. Temperror is "a transient (generally DNS) error" where "A later retry may succeed without further DNS operator action". Permerror means "the domain's published records could not be correctly interpreted", a condition that "definitely requires DNS operator intervention".
What is the SPF 10-lookup limit?
A cap on DNS work per evaluation. RFC 7208, Section 4.6.4: "The following terms cause DNS queries: the "include", "a", "mx", "ptr", and "exists" mechanisms, and the "redirect" modifier. SPF implementations MUST limit the total number of those terms to 10 during SPF evaluation, to avoid unreasonable load on the DNS. If this limit is exceeded, the implementation MUST return "permerror"."
ip4, ip6, all and the exp modifier do not count. Nested lookups do. Microsoft's documentation states the consequence plainly: "having fewer than 10 include: statements doesn't guarantee fewer than 10 DNS lookups." A single include that points to a record containing three more includes costs four. Google's help page simplifies the rule to "An SPF record can have up to 10 include: tags", which understates it: the RFC counts every DNS-querying term, at every level of nesting.
Three further limits sit in the same section:
- Void lookups. A lookup that returns no answer, or a name that does not exist (NXDOMAIN). "SPF implementations SHOULD limit "void lookups" to two." Exceeding the limit produces permerror. This limit was new in RFC 7208.
- MX address records. Evaluating an
mxmechanism must not query more than 10 A or AAAA records, otherwise the mechanism returns permerror. - Elapsed time. Receivers should cap total evaluation time, with a limit of at least 20 seconds. Exceeding it should produce temperror.
Microsoft documents what a lookup-limit failure looks like on its side: the message fails SPF with a permerror and the destination system returns a non-delivery report reading "The message exceeded the hop count" or "The message required too many lookups". Because evaluation stops at the first match, Microsoft also notes that an over-limit record can pass for some sending sources and fail for others, which shows up as intermittent failures rather than a clean break.
How do you get back under the limit?
Remove what you no longer use, and move third-party senders onto subdomains. Microsoft recommends subdomains for email services you do not control, both to preserve the reputation of the main domain and to stay under the lookup limit. M3AAWG's 2017 document also advises avoiding a and mx unless required, since each costs a lookup.
The alternative is flattening: resolving includes into literal ip4 and ip6 entries, which do not count toward the limit. dmarcian, which built a flattening feature into its own tooling, describes it as a workaround: "SPF Flattening attempts to work-around the "Too Many DNS Lookups" error without addressing the underlying issues that caused the error in the first place." Its concern is over-authorization. In its words, "unnecessary entries in SPF records create attack surfaces", because anyone who can use infrastructure listed in the record can send mail that passes. dmarcian calls flattening "a stop-gap solution".
Should an SPF record end in ~all or -all?
The two largest providers give opposite answers, and both are current.
The standard is soft on softfail by design. "A "softfail" result ought to be treated as somewhere between "fail" and "neutral"/"none". ... Receiving software SHOULD NOT reject the message based solely on this result". RFC 7208 places no normative requirement on how a receiver acts on any result.
Google recommends ~all. Its Workspace setup page: "The ~all tag tells receiving servers to mark messages as spam if they're from servers that aren't listed in the SPF record. Google recommends you use ~all in your SPF record."
Microsoft recommends -all for Microsoft 365 domains, "because we also recommend DKIM and DMARC for the domain." Microsoft's reasoning runs through DMARC. According to Microsoft, DMARC treats both -all and ~all as SPF failures, but "the DMARC policy is effectively ignored for SPF ~all failures if the messages don't also contain DKIM signatures." That is Microsoft's description of receiver behavior, and it is the basis of its recommendation.
Gmail shows what a hard fail can cost in practice. Its 550 5.7.26 rejection reads: "The (E)MAIL FROM domain [domain-name] has an SPF record with a hard fail policy (-all) but it fails to pass SPF checks with the ip: [ip-address]. To best protect our users from spam and phishing, the message has been blocked."
For historical context, M3AAWG's 2017 SPF document advised ending a record in either ~all or -all, and observed that "Most receivers treat ~all and -all similarly, although some are more likely to reject unauthenticated email when the latter is used." That reflects receiver behavior as M3AAWG described it in 2017.
Two endings are not in dispute. ?all is, per Microsoft, for testing and not recommended in production. +all authorizes every host on the internet, and M3AAWG lists it among the errors to avoid. A domain that sends no mail at all should publish v=spf1 -all, which both Microsoft and M3AAWG recommend.
Why does SPF fail on forwarded mail?
Because SPF evaluates the IP address of the server that connects to the receiver, and a forwarder is a different server. M3AAWG: "As SPF uses the IP address of a directly connected server, "SPF pass" does not survive simple relaying or re-posting through mailing list distribution or alumni-address forwarding."
RFC 7208 points to the fix in its appendix on mediators: "Mediators can solve the problem by rewriting the "MAIL FROM" to be in their own domain." The RFC does not name a scheme. The common one is the Sender Rewriting Scheme (SRS). Microsoft 365 applies it to mail forwarded to external recipients and says SRS "was added to Microsoft 365 to resolve a problem where autoforwarding is incompatible with the Sender Policy Framework (SPF)." SRS changes only the envelope address: it "doesn't affect the From address (also known as the 5322.From address or P2 sender) shown in email clients."
That fixes SPF and nothing more. Microsoft again: "SRS rewriting doesn't resolve the issue of forwarded messages not passing DMARC checks." The rewritten MAIL FROM belongs to the forwarder, so it no longer aligns with the original From domain. For forwarded mail, DMARC has to pass through DKIM, whose signature travels with the message.
Why can a domain have only one SPF record?
Because receivers cannot pick between two. RFC 7208: "A domain name MUST NOT have multiple records that would cause an authorization check to select more than one record." If more than one v=spf1 record is found, the check returns permerror. Microsoft and M3AAWG both repeat the rule. The common cause is adding a new provider by creating a second TXT record instead of adding an include to the existing one. The fix is to merge everything into a single record.
A long record is still one record. A single TXT character-string is limited to 255 octets, and RFC 7208 allows a record to be split into several strings that receivers concatenate "without adding spaces". Google advises keeping the whole TXT record within 512 bytes, and M3AAWG suggests the same size for compatibility with DNS servers that lack TCP support. M3AAWG also advises deleting obsolete v=spf2.0 records.
How do you check a domain's SPF record?
Query the TXT records at the domain and find the one that starts with v=spf1:
dig example.com TXT
nslookup -query=txt example.comThen read the result against the rules above:
- Exactly one answer begins with
v=spf1. Two or more is a permerror. - Count every
include,a,mx,ptr,existsandredirect, including those inside the records you include. More than 10 is a permerror. - Check that the MAIL FROM domain your sending platform actually uses is the domain you are querying. With an ESP's default return path, the record that matters may be theirs, not yours.
- Check the record ends in
~allor-all, and that nothing followsall.
Microsoft recommends a TTL of at least 3600 seconds on SPF TXT records.
Bounces narrow the search. Gmail returns 550 5.7.26 for a hard-fail SPF failure, 550 5.7.27 when a bulk sender fails SPF, 421 4.7.27 when it rate-limits for the same reason, and 550 5.7.24 or 451 4.7.24 when "The SPF record of the sending domain has one or more suspicious entries." These are different codes from the policy rejection covered in SMTP 550 5.7.1. The full list of SMTP error codes covers the rest.
How Trueguard handles this
SPF records are public, and they describe infrastructure. An include such as include:spf.protection.outlook.com tells anyone who queries the domain that it uses Microsoft 365. Trueguard's email validation reads the SPF record, among other DNS signals, to identify a domain's mailbox provider before it checks an address.
Frequently Asked Questions
No. SPF checks the domain in the SMTP envelope sender, the RFC5321.MailFrom, which surfaces as the Return-Path, and optionally the domain given in HELO. The visible From header is a different field. Connecting the two is the job of DMARC, which requires the SPF-authenticated domain to align with the From domain: the same Organizational Domain in relaxed mode, an identical domain in strict mode. A message can therefore pass SPF while its visible From domain is not authenticated at all.
The check returns permerror. RFC 7208 counts the include, a, mx, ptr and exists mechanisms and the redirect modifier, including those inside nested records, and requires implementations to stop at 10. The ip4, ip6 and all mechanisms do not count. Microsoft documents the resulting non-delivery report text as "The message exceeded the hop count" or "The message required too many lookups". Because evaluation stops at the first match, an over-limit record can pass for some sending sources and fail for others.
It depends on which provider's guidance you follow. Google recommends ~all. Microsoft recommends -all for Microsoft 365 domains that also use DKIM and DMARC, and states that without a DKIM signature a DMARC policy is effectively ignored for ~all failures. RFC 7208 says a softfail alone should not be grounds for rejection. Gmail does reject mail that fails SPF against a -all record, with code 550 5.7.26. A domain that sends no mail should publish v=spf1 -all.
No. M3AAWG states that "SPF fail" does not equate to malicious or bad activity, because a variety of benign reasons can cause an SPF failure. Forwarding is one: the forwarding server's IP is not listed in the original sender's record, so a legitimate message fails on the second hop. Leaving a new sending service out of the record, or exceeding the lookup limit, produces the same result for mail the domain owner did send.
