24 June 2026 · DMARC Engine · 15 min read
Sender Policy Framework (SPF) is one of the three pillars of modern email authentication, alongside DKIM and DMARC. At its core SPF answers a single, narrow question: for a given message, is the sending mail server authorised to send mail on behalf of the domain in the envelope sender address? Get SPF right and you give receivers a reliable signal that your mail is legitimate; get it wrong and you create silent deliverability failures, exceed obscure DNS limits, or, worse, publish a record that authenticates the spoofers you were trying to block.
This guide is the canonical reference for SPF as defined in RFC 7208. It covers the record syntax, every mechanism and qualifier, the all-important 10-lookup and 2-void DNS limits, the difference between permerror and temperror, when to flatten, why ~all usually beats -all, the multiple-record trap that silently breaks domains, and, crucially, how SPF feeds DMARC through alignment. If you administer a single domain or hundreds for clients, this is the depth you need.
What SPF actually checks (and what it does not)
SPF is a DNS-published list of the hosts permitted to send mail using your domain in the MAIL FROM address: the envelope sender, also called the return-path or bounce address. This is a deliberately precise scope, and misunderstanding it is the root of most SPF confusion.
SPF validates the envelope sender domain, not the visible From: header that your recipients actually read. The two are frequently different. When a marketing platform sends on your behalf, the envelope sender is usually something like bounces@mail.theplatform.com, while the From: header shows news@yourcompany.com. A plain SPF check therefore authorises the platform's domain, not yours, which is exactly why DMARC exists and why alignment (covered later) matters so much.
What SPF does:
- Lets the receiving server look up the envelope sender domain's SPF record in DNS and check whether the connecting IP is authorised.
- Produces one of several results:
pass,fail,softfail,neutral,none,permerror, ortemperror.
What SPF does not do:
- It does not encrypt, sign, or otherwise protect message content (that is DKIM's job).
- It does not, on its own, protect the address your users see in their inbox (that is DMARC's job, via alignment).
- It does not survive most forwarding scenarios, because forwarding changes the connecting IP while keeping your envelope sender (a key reason
-allcan be risky).
You can see exactly what a receiver sees for any domain with our SPF checker, which evaluates the record, counts DNS lookups, and flags errors.
Anatomy of an SPF record
An SPF record is a single DNS TXT record published at the root of the domain (the same name you would query for an A record), beginning with the version tag v=spf1. A typical record for a small business using Microsoft 365 looks like this:
v=spf1 include:spf.protection.outlook.com -all
Read left to right, this says: the version is SPF1; the hosts described by the SPF record of spf.protection.outlook.com are authorised; everything else should hard-fail.
A few non-negotiable rules from RFC 7208:
- A domain must publish at most one
v=spf1record. More than one is an automaticpermerror(see the multiple-record pitfall below). - SPF records are no longer published as DNS type SPF (type 99). That record type was deprecated by RFC 7208. Publish SPF only as a TXT record.
- A single DNS TXT string is limited to 255 characters. Longer records must be split into multiple quoted strings within the same TXT record, which DNS concatenates. Our DNS record splitter handles this correctly so you do not introduce stray spaces.
Terms: mechanisms and modifiers
An SPF record is a list of terms evaluated left to right. Terms are either mechanisms (which can match the connecting IP and produce a result) or modifiers (which change evaluation but do not match).
SPF mechanisms in full
Mechanisms are checked in order until one matches. When a mechanism matches, its qualifier determines the result and evaluation stops.
all: Always matches. It is the catch-all and should always be the last mechanism. Its qualifier (-,~,?, or+) defines the policy for every host not already matched.ip4: Matches a literal IPv4 address or CIDR range, e.g.ip4:192.0.2.0/24. Costs zero DNS lookups, which makes it the cheapest and most reliable mechanism.ip6: Asip4, but for IPv6, e.g.ip6:2001:db8::/32. Also zero lookups. If you send over IPv6 at all, you must authorise those addresses too, or IPv6 mail will fail SPF.a: Matches if the connecting IP is one of the domain's A/AAAA records.aalone uses the current domain;a:mail.example.comchecks a named host. Costs one DNS lookup.mx: Matches if the connecting IP is one of the domain's MX hosts. Costs one lookup for the MX query plus a lookup for each MX host's address. This can be surprisingly expensive.include: Recursively evaluates another domain's SPF record, e.g.include:_spf.google.com. If that included record passes, the include matches. Eachincludecosts one lookup, and any lookups inside the included record count against your total too. This is the single biggest cause of over-limit records.exists: Matches if a DNS A lookup of the (often macro-expanded) domain returns any answer. Costs one lookup. Used for advanced, per-sender authorisation schemes.ptr: Matches based on a reverse-DNS lookup of the connecting IP. Deprecated. RFC 7208 explicitly saysptrSHOULD NOT be used: it is slow, unreliable, and a burden on DNS infrastructure. Remove it if you find it.
Mechanisms that cost DNS lookups
Memorise this list, because it is the basis of the 10-lookup limit: include, a, mx, ptr, and exists all consume DNS lookups. The ip4, ip6, and all mechanisms, and the modifiers, do not.
Qualifiers: the four prefixes
Every mechanism carries an implicit or explicit qualifier that determines the result when it matches:
+(Pass): The default if no qualifier is written.mxand+mxare identical. A matching host is authorised.-(Fail / hardfail): A matching host is explicitly not authorised. Used as-allto assert "anything not listed is forged."~(SoftFail): A matching host is probably not authorised, but the receiver should accept the message and mark it accordingly. Used as~all.?(Neutral): Explicitly no assertion either way. Used as?allduring very early testing; offers no protection.
The qualifier you put on all is the most consequential single character in your SPF record. We return to the ~all vs -all debate below.
The 10-lookup limit: the rule that breaks the most records
This is the constraint that catches almost every growing organisation, and the one that justifies an entire tooling category. RFC 7208 mandates that evaluating an SPF record must require no more than 10 DNS-querying mechanisms. Specifically, the mechanisms include, a, mx, ptr, and exists, plus the redirect modifier, each count. Crucially, this is counted recursively: every lookup inside every included record counts against your single budget of 10.
Exceed it and the result is permerror, a permanent error. Many receivers treat permerror the same as no SPF at all, or worse, as a fail. For DMARC purposes, a permerror means SPF does not pass, so if DKIM also fails to align, the message fails DMARC.
A worked example shows how fast you blow the budget. Suppose you publish:
v=spf1 include:_spf.google.com include:spf.protection.outlook.com include:servers.mcsv.net include:_spf.salesforce.com include:sendgrid.net -all
Five include terms looks harmless. But _spf.google.com itself contains further includes (it nests two or three levels deep), and so does each vendor record. It is entirely normal for that five-include record to resolve to 15-20 actual DNS lookups, well over the limit. The record looks fine to the human eye and resolves fine in a quick dig, but real receivers count the nested lookups and return permerror.
There is a second, less-known limit alongside it.
The 2-void-lookup limit
RFC 7208 also caps void lookups (DNS queries that return an empty answer, NXDOMAIN or NOERROR with no records) at two. Exceed two void lookups during evaluation and you get permerror. This bites when an include points at a vendor subdomain that no longer exists, or when an mx/a mechanism references a host that has been decommissioned. Stale includes for cancelled services are the usual culprit. Audit and remove vendors you no longer use.
To see your real, recursively-expanded lookup count and any void lookups before they cause problems, run your domain through the SPF checker.
permerror vs temperror: know the difference
These two error results sound similar but mean very different things, and confusing them leads to the wrong fix:
permerror(permanent error): The record itself is broken or unprocessable: too many DNS lookups, more than two void lookups, multiple SPF records, invalid syntax, or a deprecated/illegal term. The fix is to change your record. Retrying will never help.temperror(temporary error): A transient DNS problem occurred during evaluation, such as a timeout, a SERVFAIL, or an unreachable nameserver. The fix is usually on the DNS-resolution side; receivers typically defer (4xx) and retry. If you see intermittenttemperrorin your DMARC reports, investigate nameserver reliability and response times rather than the SPF syntax.
Both permerror and temperror are "SPF did not pass" outcomes for DMARC. A persistent permerror is a launch blocker for any move toward enforcement.
includes vs ip4: choose deliberately
A recurring design decision is whether to authorise a sender by include: (delegating to the vendor's own SPF record) or by hard-coding ip4:/ip6: ranges. Both are valid; the trade-offs matter.
Use include: when:
- The vendor publishes and maintains an SPF record (most major ESPs and platforms do).
- The vendor rotates or expands its sending IPs frequently. The include tracks those changes automatically, so you never have to update your record when they add a server.
Use ip4:/ip6: when:
- You operate your own static mail servers with fixed addresses.
- You are fighting the 10-lookup limit and need to eliminate DNS-querying mechanisms (each literal IP costs zero lookups).
The tension is clear: includes are low-maintenance but expensive in lookups; literal IPs are lookup-free but high-maintenance, because if the vendor changes their IPs and you have hard-coded them, your mail starts failing SPF with no warning. This trade-off is precisely what flattening tries to resolve.
SPF flattening: when, how, and the catch
SPF flattening is the process of recursively resolving all of your include, a, and mx mechanisms down to their underlying IP ranges and republishing those as ip4:/ip6: literals. The result is a single record with zero DNS-querying mechanisms, which sidesteps the 10-lookup limit entirely.
A flattened record might collapse the five-include example above into something like:
v=spf1 ip4:209.85.128.0/17 ip4:40.92.0.0/15 ip4:198.2.128.0/18 ip4:136.147.128.0/20 ip4:167.89.0.0/17 -all
Flattening is a genuinely useful technique, but it has one serious catch you must plan for: vendors change their IP ranges, and a static flattened record does not know when they do. The moment a vendor adds a sending IP that is not in your frozen list, that mail fails SPF, silently. For this reason, flattening should never be a one-off manual edit. It needs continuous monitoring that re-resolves the source records and updates your published record when the underlying IPs change.
This is exactly the gap our hosted SPF service closes: we keep a flattened, lookup-safe record published for you and update it automatically whenever a source provider changes its ranges, so you get the lookup safety of flattening without the silent-failure risk.
Other ways to stay under the limit, in rough order of preference:
- Remove unused includes. The cheapest fix. Audit every vendor; delete any you no longer send through. This also clears stale void lookups.
- Replace
mxwith explicitip4if your mail servers have stable addresses. - Consolidate vendors so you depend on fewer sending platforms.
- Flatten the rest with automated monitoring.
~all vs -all: softfail or hardfail?
The qualifier on your terminal all mechanism is a real policy decision, not a stylistic one.
-all(hardfail) tells receivers that any host not listed is definitively unauthorised and the message should be rejected or treated as forged.~all(softfail) tells receivers the message is probably unauthorised but should be accepted and marked, not rejected outright.
It is tempting to assume -all is "more secure" and reach for it immediately. In practice, the safer sequence (especially before DMARC enforcement) is more nuanced:
- Forwarding breaks SPF. When a recipient forwards your mail (or it passes through a mailing list), the connecting IP becomes the forwarder's, which is not in your SPF record. With
-all, that legitimate forwarded mail hard-fails and may be rejected. With~all, it softfails and usually still gets delivered, and DMARC can still pass on the DKIM leg if your messages are signed. - Incomplete inventory. Most organisations discover sending sources they had forgotten: a billing system, a CRM, an old monitoring box. Publishing
-allbefore you have confirmed every legitimate sender risks blocking your own mail.
The pragmatic guidance:
- Start with
~allwhile you build a complete picture of your senders using DMARC aggregate reports. - Once your reports show every legitimate source authenticating cleanly, you can tighten to
-allif you wish, but understand that under a DMARC policy ofp=reject, DMARC already provides the strong rejection signal, and a well-signed DKIM record protects forwarded mail in a way SPF cannot. Many mature setups stay on~alldeliberately and lean on DMARC + DKIM for enforcement. The enforcement journey explains how to sequence this safely.
The multiple-record pitfall
This one is worth its own warning because it silently and completely breaks SPF. A domain may publish only one v=spf1 record. If a resolver finds two or more TXT records beginning with v=spf1, the result is permerror: SPF effectively stops working for the whole domain.
This happens constantly during onboarding. A team adds Microsoft 365 and pastes in a second SPF record rather than merging the new include into the existing one:
v=spf1 include:_spf.google.com ~all
v=spf1 include:spf.protection.outlook.com ~all
The fix is to merge all mechanisms into a single record:
v=spf1 include:_spf.google.com include:spf.protection.outlook.com ~all
Note that this only applies to v=spf1 records. You can, and often will, have other unrelated TXT records at the same name (DKIM uses a different name; DMARC lives at _dmarc; verification tokens are separate). Only duplicate v=spf1 records are the problem. Building the record with our SPF generator helps you produce a single, correctly-merged record from the start.
SPF and DMARC: the alignment that ties it together
Here is the most important conceptual leap, and the reason a passing SPF check is necessary but not sufficient. DMARC does not care merely that SPF passed; it cares that SPF passed and is aligned with the domain your recipients see in the From: header.
SPF alignment compares the domain used in the SPF check (the envelope sender / MAIL FROM domain) with the From: header domain. DMARC offers two alignment modes:
- Relaxed alignment (the default): the two domains need only share the same organisational domain. So an envelope sender of
bounces.mail.yourcompany.comaligns with a From: ofyourcompany.com. - Strict alignment: the two domains must match exactly.
This is why the marketing-platform example from the start matters. If your ESP sends with an envelope sender of bounces@mail.theplatform.com, SPF passes for theplatform.com, but that does not align with your From: of yourcompany.com. SPF therefore contributes nothing to DMARC for that mail stream. The fix is either a custom return-path / custom MAIL FROM at the vendor (so the envelope sender becomes a subdomain of your domain) or relying on aligned DKIM signing instead.
The practical consequences:
- DMARC passes if either SPF or DKIM passes and aligns. You do not need both, but you need at least one aligned mechanism.
- Because SPF alignment depends on the return-path, which many third-party senders control, DKIM is often the more reliable of the two alignment paths. Configure both; do not rely on SPF alone.
You can confirm alignment for any mail stream by reading your DMARC aggregate reports, which our DMARC report analyser parses into per-source pass/fail and alignment detail.
A practical SPF checklist
Use this sequence whether you are setting up a new domain or auditing an existing one:
- Inventory every sender. List every system that sends mail as your domain: mail platform, ESP, CRM, ticketing, billing, monitoring, transactional API. DMARC aggregate reports are the most reliable way to discover the ones you forgot.
- Build one merged record. Combine all sources into a single
v=spf1TXT record. Use the SPF generator. - Authorise IPv6 too if you send over it.
- Count your real lookups. Recursively expand and confirm you are at or under 10, with no more than two void lookups. Use the SPF checker.
- Remove the dead weight. Delete unused includes, kill any
ptrmechanism, and replace expensivemxwithip4where addresses are stable. - Flatten what remains if you are still over the limit, with automated monitoring, never a frozen manual edit.
- Start on
~all. Tighten to-allonly after reports prove every legitimate source authenticates, and only if you understand the forwarding trade-off. - Set custom return-paths at third-party senders so SPF aligns for DMARC, and pair SPF with aligned DKIM.
- Layer DMARC on top and progress through monitoring to enforcement.
Next steps
SPF is deceptively simple to read and surprisingly easy to break. The recurring failure modes are always the same: blowing the 10-lookup limit through nested includes, publishing two v=spf1 records, leaving stale includes that trip the void-lookup cap, hard-coding vendor IPs that later change, and assuming a passing SPF check protects the address your users actually see when alignment is what really matters.
Start by checking what you publish today with the SPF checker, then build a clean, merged, lookup-safe record with the SPF generator. When you are ready to stay under the limit permanently, hosted SPF keeps a flattened record updated automatically, and the broader DMARC platform ties SPF, DKIM and alignment together so you can move to enforcement without breaking legitimate mail. For the full sequence from monitoring to p=reject, follow the enforcement journey.