DomainOpsDomainOps

SPF record explained: syntax, qualifiers and the 10-lookup trap

SPF record explained for practitioners: v=spf1 syntax, include vs ip4, ~all vs -all, the 10-DNS-lookup limit, and the mistakes that cause permerror.

DomainOps Team··6 min read

If you want an SPF record explained without the marketing gloss, here it is: SPF is a TXT record on your domain that lists which servers are allowed to send email claiming to come from that domain. Receiving mail servers look up the record, check the connecting IP against the list, and produce a result — pass, fail, softfail, or one of several error states. That result feeds into spam filtering and, increasingly, into DMARC. The record itself is one line of text, but the syntax has enough sharp edges that a large fraction of production SPF records are subtly broken.

DomainOps email-auth audit per-domain results showing SPF, DKIM and DMARC pass/fail pills, with one row expanded to reveal the actual SPF record
The free DomainOps email-auth audit shows each domain's SPF/DKIM/DMARC status and the underlying records.

The anatomy of an SPF record

An SPF record is a single TXT record at the apex of the domain (or on a subdomain, if that subdomain sends mail). A typical one looks like this:

txt
example.com.  TXT  "v=spf1 ip4:203.0.113.0/24 include:_spf.google.com mx ~all"

Reading left to right:

ElementMeaning
v=spf1Version tag. Must be first, exactly this string.
ip4:203.0.113.0/24Authorise this IPv4 address or CIDR range. ip6: is the equivalent for IPv6.
include:_spf.google.comFetch the SPF record of another domain and evaluate it. If that record passes, this mechanism matches.
mxAuthorise the IPs of the domain's MX hosts.
aAuthorise the IPs the domain's A/AAAA records resolve to.
~allCatch-all: everything not matched above gets a softfail.

Mechanisms are evaluated left to right, first match wins. Once a mechanism matches, evaluation stops and the qualifier on that mechanism becomes the result. Nothing after the match is ever looked at — which is why all only makes sense as the final mechanism, and anything written after it is dead text.

Qualifiers: ~all vs -all (and why +all is a liability)

Each mechanism carries one of four qualifiers, with + (pass) being the implicit default:

QualifierResult on matchTypical receiver behaviour
+passAccept as authorised
-failStrong signal to reject or junk
~softfailAccept but treat with suspicion; counts as fail for DMARC
?neutralNo opinion either way

The qualifier you'll agonise over is the one on all. ~all says "anything else is probably not us"; -all says "anything else is definitely not us". In a pre-DMARC world the difference mattered a great deal. Under DMARC, both softfail and fail count as an SPF failure for alignment purposes, so the practical gap has narrowed — many teams run ~all indefinitely and let DMARC do the enforcement.

What you must never publish is +all. It authorises the entire internet to send as your domain, which makes the record worse than useless: spammers get an SPF pass on forged mail. It still turns up in the wild, usually as a "temporary" fix for a delivery problem that someone forgot to revert.

The 10-DNS-lookup limit

RFC 7208 caps SPF evaluation at 10 DNS-querying mechanisms per check. The mechanisms that count are include, a, mx, ptr, exists and the redirect modifier. ip4 and ip6 are free, because they need no lookup. Crucially, the count is recursive: an include that itself contains three includes costs four lookups, not one.

Exceed the limit and the result is permerror — a permanent error that most receivers treat as a failure, and that DMARC treats as no SPF result at all. This is the most common way a working email setup silently degrades: you add a CRM, a helpdesk tool and a marketing platform over eighteen months, each adds an include, and one day the nested lookups tip past ten.

This is why SPF flattening exists: replacing include mechanisms with the literal ip4/ip6 ranges they resolve to, bringing the lookup count back down. It works, but it trades one problem for another — providers change their sending ranges without notice, so a flattened record goes stale unless something re-resolves and republishes it regularly. If you flatten by hand, you've signed up for a recurring maintenance task. There is also a second, lesser-known limit: no more than two "void lookups" (queries returning NXDOMAIN or an empty answer) per evaluation, which catches records referencing decommissioned services.

Common mistakes worth checking for today

Multiple SPF records. A domain must have exactly one TXT record starting with v=spf1. Two or more is an automatic permerror — receivers don't pick one or merge them, they fail the check outright. This usually happens when a second team or a SaaS onboarding guide says "add this TXT record" and nobody checks whether one already exists. The fix is to merge the mechanisms into a single record.

all too early, or trailing junk after it. Everything after the first matching mechanism is ignored, so v=spf1 ~all include:_spf.google.com softfails all your legitimate mail.

ptr mechanisms. Deprecated by RFC 7208 — slow, unreliable, and several large receivers skip or penalise them. Replace with ip4/ip6 ranges.

SPF on the apex but not on sending subdomains. SPF applies to the exact domain in the envelope-from. If notify.example.com sends mail, it needs its own record; the apex record does not cascade down.

Forgetting that forwarding breaks SPF. A message forwarded by a mailing list or a personal forwarding rule arrives from the forwarder's IP, which your record doesn't authorise. This is by design and is precisely why DMARC accepts either SPF or DKIM alignment — DKIM survives forwarding, SPF doesn't.

Keeping it from rotting

An SPF record is correct on the day you publish it and decays from there: providers re-IP, includes get added, old services linger in the record years after the contract ended. The useful discipline is to treat the record as monitored infrastructure rather than set-and-forget DNS — re-validate the lookup count, check for duplicate records, and confirm every include still resolves.

DomainOps runs SPF, DKIM and DMARC health checks continuously across every domain you monitor and alerts you when a record breaks or drifts. If you just want a one-off check, run your domain through our free email auth audit.

spfemaildns