Reference

SPF mechanisms, modifiers and qualifiers

An SPF record is `v=spf1` followed by terms evaluated left to right. RFC 7208 defines eight mechanisms — all, include, a, mx, ptr, ip4, ip6 and exists — that match the sending address, two modifiers — redirect and exp — that change what happens around the match, and four qualifiers that set the result of a match: + pass, - fail, ~ softfail and ? neutral. Five mechanisms and the redirect modifier cost a DNS lookup, and a record may not need more than ten of them.

The terms of an SPF record as defined by RFC 7208 (sections 4.6, 5 and 6), and what the VeriFixScan SPF parser does with each.
TermKindWhat it doesDNS lookupWhat the scan checks
allMechanismAlways matches. Placed last, its qualifier decides the result for every sender not matched before it.NoIts qualifier is graded; terms written after it are flagged because they are never evaluated.
includeMechanismEvaluates another domain's SPF record and matches if that evaluation passes.Yes, plus every lookup of the included recordRequires a valid domain; its tree is walked to count lookups.
aMechanismMatches if the sender's address is one of the A or AAAA addresses of the domain, optionally widened by a CIDR length.YesValidates the domain if one is given; counted as a lookup.
mxMechanismMatches if the sender's address is one of the addresses of the domain's mail exchangers.Yes, plus a lookup per exchangerValidates the domain if one is given; counted as a lookup.
ptrMechanismMatches through a reverse lookup of the sender's address. RFC 7208 says it should not be published.YesFlagged as deprecated.
ip4MechanismMatches if the sender's address is in the given IPv4 address or range.NoValidates the address and prefix length.
ip6MechanismMatches if the sender's address is in the given IPv6 address or range.NoValidates the address and prefix length.
existsMechanismMatches if an A lookup of the given domain, usually built with macros, returns any address.YesRequires a domain; macros are accepted without being expanded.
redirect=ModifierWhen nothing in the record matched, replaces the evaluation with another domain's record. Ignored if the record contains all.YesMust name a valid domain and appear once; flagged when all makes it useless.
exp=ModifierNames a domain whose TXT record holds an explanation returned with a fail result.Only when a fail result is explainedRecorded; its content is not fetched.
+QualifierPass: the sender is authorised. The default when no qualifier is written.No+all is reported as a failure: it authorises every server on the Internet.
-QualifierFail: the sender is explicitly not authorised.No-all is the result reported as correct.
~QualifierSoftfail: the sender is probably not authorised; receivers usually accept and mark the message.No~all is reported for information, as a partial restriction.
?QualifierNeutral: the record makes no statement about the sender.No?all is reported as a warning.

How a receiver evaluates the terms

The receiver fetches the TXT record that starts with `v=spf1` and walks the mechanisms from left to right. The first one that matches the sending address decides the result, through its qualifier; nothing after it is read.

When no mechanism matches, a `redirect=` modifier hands the whole evaluation to another domain's record. Without one, the result is neutral — which is why a record should end with an explicit `all`.

Modifiers are not positional: `redirect=` and `exp=` apply to the record as a whole wherever they are written. Unknown modifiers are ignored by receivers, so a misspelled one fails silently.

A domain must publish one SPF record. Two records starting with `v=spf1` make the evaluation return a permanent error, which receivers treat as having no usable policy.

The ten-lookup budget

RFC 7208 caps the terms that trigger DNS queries — `include`, `a`, `mx`, `ptr`, `exists` and `redirect=` — at ten per evaluation, counting the ones inside included records. Past ten, the result is a permanent error.

`ip4`, `ip6` and `all` cost nothing, which is why replacing an `include` of a provider by the provider's published address ranges is a common way to get back under the limit — at the price of maintaining the ranges by hand.

The specification also limits "void" lookups, the ones that return no answer, to two, so an `include` of a domain that no longer publishes SPF costs more than it appears to.

What a scan validates, and what it does not

The SPF parser recognises the eight mechanisms and two modifiers above; any other mechanism name is an error, and any other modifier a warning. It validates the domain of `include`, `exists`, `redirect=` and of `a` or `mx` when one is given, and the address and prefix length of `ip4` and `ip6`.

`email.spf.lookups` walks the include and redirect tree over real DNS to count lookups, with bounds on depth and queries; when a bound is hit the count is reported as incomplete rather than guessed. `email.spf.all` grades the final qualifier, `email.spf.ptr` flags `ptr`, `email.spf.single` flags a second record and `email.spf.syntax` reports parse errors.

Macros such as `%{i}` are legal and are accepted as written; the scan does not expand them, so a record built mostly from macros is checked for structure, not for what it resolves to.

Frequently asked questions

Should an SPF record end with -all or ~all?
-all states that unlisted servers are not authorised, which is the precise statement once every legitimate sender is listed. ~all is a softer signal often used during a rollout. With an enforcing DMARC policy in place, receivers act on DMARC, and the difference matters less.
Does ip4 count toward the ten-lookup limit?
No. ip4, ip6 and all require no DNS query. Only include, a, mx, ptr, exists and the redirect modifier count, including those reached inside included records.
Can a domain publish two SPF records?
No. With two TXT records starting with v=spf1, evaluation ends in a permanent error. Senders must be merged into a single record.

Sources

Related

VeriFixScan crawls a site and applies its checks to every page it reaches, keeping the evidence behind each finding. Scanning one website is free.

Scan a website