An SPF record is a DNS TXT record, published at a domain and starting with v=spf1, that lists the IP addresses and services allowed to send email using that domain in the SMTP envelope sender. A receiving mail server compares the connecting IP against the list and returns pass, fail, softfail or neutral. SPF is defined in RFC 7208 (April 2014), and a record with a duplicate or more than 10 DNS lookups returns permerror and authorizes nobody.
At a glance
- Where the record lives: One TXT record at the name used in the envelope sender (
example.com, or a sending subdomain), TXT only per RFC 7208 section 3.1, which dropped the dedicated SPF record type (99) from SPF version 1 - Record format:
v=spf1first, then mechanisms left to right, each with an optional qualifier, ending inallorredirect= - What it authenticates: The domain in the envelope sender (RFC5321.MailFrom, seen as Return-Path) and the HELO hostname
- What it does not authenticate: The From header a recipient reads, the message content, or mail that has been forwarded
- The limit: 10 DNS lookups per evaluation (RFC 7208 section 4.6.4); over the limit returns permerror
What is an SPF record for: naming the servers allowed to use a domain in the envelope sender
An SPF record answers one question a receiving server asks during the SMTP handshake: is the IP address connecting to me allowed to use this domain in the MAIL FROM command? That domain is the envelope sender, called RFC5321.MailFrom in the specification and visible after delivery as the Return-Path header. It is the address bounces go to, and it is often different from the From address a person sees.
RFC 7208 section 2.3 recommends checking the HELO hostname the sending server announces, and section 2.4 requires verifiers to check the MAIL FROM identity unless the HELO check already produced a definitive result. When the envelope sender is empty, as it is for bounce notifications, the verifier checks postmaster@ the HELO hostname instead.
The SPF result is a statement about authorization, not about the message. Section 2.6 of RFC 7208 lists the possible results: none, neutral, pass, fail, softfail, temperror and permerror. The overview of how SPF, DKIM and DMARC work together covers where that result goes next.
SPF record format: version, mechanisms, qualifiers and modifiers
An SPF record is a single string of space-separated terms, and the terms fall into three kinds: a version tag, mechanisms and modifiers. The version tag v=spf1 must be the first term. RFC 7208 section 4.5 discards any TXT record that does not begin with exactly v=spf1, so v=spf10 breaks the record, and so does anything placed before the version tag.
Mechanisms are tests against the connecting IP, evaluated left to right. RFC 7208 section 4.6.2 states that when a mechanism matches, processing stops and that mechanism's qualifier becomes the result; when it does not match, evaluation moves to the next term. The first match wins, so order matters. The eight mechanisms are all, include, a, mx, ptr, ip4, ip6 and exists.
Qualifiers are a single character in front of a mechanism that decides the result when it matches:
| SPF qualifier | Result when the mechanism matches | Typical use |
+ (default when omitted) | pass | ip4:203.0.113.10 and +ip4:203.0.113.10 mean the same thing |
- | fail | -all to reject every unlisted sender |
~ | softfail | ~all during rollout, before every sender is confirmed |
? | neutral | ?all to express no opinion; rarely appropriate |
Modifiers are name=value pairs that add information rather than testing the IP. RFC 7208 defines two: redirect= hands the evaluation to another domain's record when nothing else matched, and exp= names a TXT record whose text is returned as the explanation for a fail. Section 6 allows each once; a duplicate produces permerror.
If no mechanism matches and there is no redirect=, RFC 7208 section 4.7 returns neutral, as if ?all had been written. A record with no all term therefore protects nothing.
An annotated SPF record example, mechanism by mechanism
A working SPF record example for a domain that sends through Google Workspace, a marketing platform and one self-hosted server looks like this:
example.com. IN TXT "v=spf1 ip4:203.0.113.10 ip6:2001:db8::/32 a mx include:_spf.google.com include:mail.marketingvendor.example -all"
Read left to right: version 1; the IPv4 address 203.0.113.10 may send; any address in the IPv6 block 2001:db8::/32 may send; the A and AAAA records of example.com may send; the MX hosts of example.com may send; anything Google's or the marketing vendor's SPF record authorizes may send; everything else fails.
| SPF mechanism | What it matches | Counts toward the 10-lookup limit |
ip4:<address or CIDR> | The connecting IP is the address, or is inside the IPv4 range | No |
ip6:<address or CIDR> | The connecting IP is the address, or is inside the IPv6 range | No |
a or a:<domain> | The connecting IP is one of the A or AAAA records of the domain (defaults to the current domain) | Yes |
mx or mx:<domain> | The connecting IP is an address of one of the domain's MX hosts; each MX may resolve to at most 10 addresses | Yes |
include:<domain> | The other domain's SPF record returns pass for this IP; a fail there does not fail the outer record, evaluation just continues | Yes, plus every lookup the included record performs |
exists:<domain> | An A record exists for the constructed name, usually built with macros | Yes |
ptr or ptr:<domain> | The reverse DNS name of the IP resolves back to it and ends in the domain; RFC 7208 section 5.5 says it SHOULD NOT be published | Yes |
all | Always matches; used last as the default | No |
redirect=<domain> (modifier) | Replaces the evaluation with the other domain's record when no mechanism matched; ignored if all is present | Yes |
The a and mx terms in the example are redundant if the same hosts are already listed by IP, and each spends a lookup. include asks whether the other record would pass this IP; only a pass counts.
The SPF 10 DNS-lookup limit in RFC 7208, and why a record over it authorizes nobody
RFC 7208 section 4.6.4 caps the terms that trigger DNS queries at 10 per evaluation, and an implementation that exceeds the cap MUST return permerror. The counted terms are the include, a, mx, ptr and exists mechanisms and the redirect modifier. The ip4, ip6 and all mechanisms and the exp modifier do not cause a lookup during evaluation and are not counted.
The count is cumulative through the include chain. include:_spf.vendor.example spends one lookup itself, then every include, a and mx inside the vendor's record adds to the same total. Five SaaS senders with two or three nested includes each can pass 10 with only five terms in the published record.
Key insight
An SPF record over 10 lookups authorizes nobody.
Permerror is the RFC 7208 section 2.6.7 result for a record that could not be interpreted, not a pass for the senders that were listed. Under DMARC, permerror is not a pass, so every message from that domain depends on DKIM alone.
Section 4.6.4 adds two smaller limits. Each mx term may cause at most 10 address lookups, and implementations SHOULD limit void lookups, queries that return no records or a name error, to two, with permerror beyond that. Two a or exists terms that return nothing can break a record that is otherwise well under 10.
How to keep an SPF record under 10 lookups
Staying under the 10-lookup limit means spending lookups only on senders that need them, and four techniques cover most cases.
- Remove includes for services that no longer send. A vendor dropped two years ago is still costing lookups, and its record may have grown since.
- Replace stable includes with the provider's published IP ranges.
ip4andip6terms cost nothing. This is called flattening, and its cost is maintenance: when the provider changes its ranges the flattened record is stale, so it suits providers with rarely changing ranges rather than large platforms that shift addresses. - Give third-party senders a subdomain. A marketing platform sending with
news.example.comin the envelope gets its own SPF record there, with its own 10-lookup budget, and a vendor's include chain growing past the limit affects only that subdomain. - Drop
a,mxandptrwhere they add nothing. If the mail servers are already listed by IP,aandmxare duplicates.ptrshould not be published at all.
SPF macros, defined in RFC 7208 section 7, substitute connection details such as the sending IP into a domain name, typically with exists:, so one lookup can answer for many senders. Some managed SPF services use them to sidestep the limit; a domain with a handful of senders rarely needs them.
-all vs ~all vs ?all in an SPF record, and how DMARC changes the choice
The final all term decides what happens to a server the record did not list, and the three usual qualifiers ask for three different treatments. -all returns fail, an explicit statement the server is not authorized. ~all returns softfail, which RFC 7208 section 2.6.5 calls a weak statement that the host is probably not authorized. ?all returns neutral, no opinion either way.
RFC 7208 leaves the handling of every result to the receiver. Section 8.4 leaves the handling of fail to local policy and says a receiver that chooses to reject during the SMTP transaction SHOULD use a 550 reply; section 8.5 says receivers SHOULD NOT reject solely on softfail. A domain with ~all and no DMARC record has told receivers little more than one with ?all.
DMARC does not look at the qualifier. RFC 9989 sections 4.4.2 and 5.3.5 count SPF toward a DMARC pass only when it produces a pass result for the MAIL FROM domain and that domain aligns with the From header; fail, softfail, neutral, none and permerror are all the same to DMARC. Once a domain publishes DMARC at quarantine or reject, the DMARC policy, not the SPF qualifier, is what a receiver's local policy consults for an unlisted sender.
Key insight
With DMARC enforced, the DMARC policy decides what happens to unlisted senders; the SPF qualifier only matters at receivers that stop at SPF.
-all is still the target state, because filters that apply SPF without DMARC may treat fail and softfail differently, and security questionnaires often ask whether the record ends in -all. Publish ~all while DMARC aggregate reports confirm every legitimate sender, then move to -all. The guide to what DMARC is covers alignment and the policy levels in full.
What an SPF record does not do, and why DKIM and DMARC are still needed
An SPF record authenticates an address the recipient never reads, and that one fact explains its three gaps. Nothing in RFC 7208 ties the envelope sender to the From header: an attacker can send from a domain they control, pass SPF for it, and put any other domain in the From field, which RFC 7208 section 11.2 acknowledges directly.
Forwarding is the second gap. A forwarding server connects to the final receiver with its own IP, which is not in the original domain's SPF record, so SPF fails for legitimate mail. Message content is the third: SPF makes no statement about headers or body, so a passing message may have been altered or carry a forged Reply-To.
DKIM (RFC 6376) closes the content gap by signing the message with a key the domain publishes, and the signature survives forwarding because it travels with the message. DMARC closes the From-header gap by requiring the SPF or DKIM domain to align with the From domain and by publishing a policy for failures. The companion post on what DKIM is covers the signature and its keys.
Common SPF record mistakes that produce permerror or no protection
The SPF record mistakes below each produce either a permerror, which authorizes nobody, or a record that is valid and protects nothing.
- Two SPF records at one name. RFC 7208 section 4.5 returns permerror when more than one
v=spf1record is found. It commonly happens when a provider's setup guide says add this TXT record and the include is added as a second record instead of merged into the existing one. +allat the end. It matches every IP with a pass, so every server on the internet is authorized. It typically gets added in a hurry to fix a deliverability problem and stays.- No
allterm and noredirect=. The default result is neutral, so the record expresses no policy. ptrmechanisms. RFC 7208 section 5.5 says they SHOULD NOT be published; they are slow, unreliable and spend lookups.- A typo in an include domain.
include:_spf.googel.compoints at a name with no SPF record, and RFC 7208 section 5.2 turns that into permerror for the whole evaluation. Section 4.6.4 separately caps void lookups at two. - More than 10 lookups. Permerror, and no sender is authorized.
- A string over 255 characters. DNS caps each character-string in a TXT record at 255 octets. RFC 7208 section 3.3 says a longer record is split into multiple quoted strings that the receiver concatenates without adding spaces; a DNS console that does not split the string may truncate or reject it. Section 3.4 says the DNS answer SHOULD fit in 512 octets and gives 450 octets, counting the name and every TXT record at that name, as the working guideline.
redirect=alongsideall. RFC 7208 section 5.1 ignores anyredirectwhenallis present.
Key insight
Two SPF records is the same as none.
A domain that adds a second record to onboard a vendor has, from that moment, no working SPF for any sender, including the ones that were fine before.
How to check and generate an SPF record
An SPF record can be checked with a DNS query for the domain's TXT records, and a purpose-built tool saves the lookup arithmetic. The SPF checker reads a domain's public TXT records over DNS over HTTPS, picks out the v=spf1 record, reports which services it authorizes, how it ends and how many lookups it consumes, and sends no mail. Each finding is mapped to SOC 2 CC6.1, CC6.7 and CC7.2 and ISO 27001:2022 A.5.14 and A.8.20.
The SPF record generator assembles a record in the browser from the mechanisms added, counts lookups as each one goes in and shows when the total approaches the 10-lookup limit; the finished string is copied into DNS as a TXT record on the domain itself. Once it is live, the DMARC checker shows whether a DMARC record exists at _dmarc.<domain> and which policy it declares. The free security tools hub collects the SPF, DKIM, DMARC and MX lookups in one place.
Bring the SPF record under the limit
Truvo consolidates senders as part of an effective security program and hands over the evidence.
Frequently asked questions about SPF records
What is an SPF record example?
A minimal SPF record for a domain that sends only through Google Workspace is v=spf1 include:_spf.google.com -all. It authorizes every server Google lists in its own SPF record and fails everything else. A domain that also sends from its own server adds an ip4: term before the -all.
How many SPF records can a domain have?
Exactly one. RFC 7208 section 4.5 says a name with more than one record starting v=spf1 produces permerror, so none of the listed senders are authorized. Every provider's include belongs in that one record. A subdomain that sends with its own name in the envelope gets its own single record.
What does -all mean in an SPF record?
-all is the all mechanism with the fail qualifier. It always matches, so a server that reached it without matching an earlier mechanism receives an SPF fail, an explicit statement from the domain owner that the server is not authorized. ~all returns softfail instead, and ?all returns neutral.
What is the SPF 10 lookup limit?
RFC 7208 section 4.6.4 limits the include, a, mx, ptr and exists mechanisms and the redirect modifier to 10 DNS lookups per evaluation, counted through every nested include. ip4, ip6 and all are not counted. Exceeding the limit returns permerror, which means the record authorizes no one.
Does SPF work without DMARC?
Yes, in the sense that receivers still evaluate the record and return a result. But SPF alone covers only the envelope sender, and RFC 7208 defines no required handling for any result, so a receiver may accept a message that failed SPF. DMARC ties the SPF result to the From address a recipient sees and tells receivers what to do when it fails.
Ready to Start Your Compliance Journey?
Get a clear, actionable roadmap with our readiness assessment.
Contact Us
Free Report: The CPCSC Compliance Playbook
Join the waitlist for a free copy when it's released.
About the Author
Former security architect for Bank of Canada and Payments Canada. 20+ years building compliance programs for critical infrastructure.