SPF Record Syntax: Mechanisms, Qualifiers, Modifiers, and Macros

meysamazad1 pts0 comments

SPF Record Syntax: The Complete Reference (RFC 7208) | DMARCguard Skip to main content<br>22 min read Share

SPF Record Syntax: Mechanisms, Qualifiers, Modifiers, and Macros<br>SPF record syntax follows one shape: a single DNS TXT record that starts with v=spf1, followed by space-separated terms — mechanisms with optional qualifiers, then modifiers — evaluated left to right until the first match. Here is a complete record:<br>v=spf1 ip4:192.0.2.0/24 include:_spf.example.com -all That one line authorizes a /24 network and a third party’s servers, then fails everything else. Every rule governing it lives in RFC 7208, the SPF standard published in April 2014.<br>This page is the full reference: every mechanism, every qualifier, both modifiers, the complete macro table, evaluation order, DNS lookup limits, and record placement rules — each with the RFC 7208 section that defines it. If you want protocol fundamentals first — why SPF exists and how it fits with DKIM and DMARC — start with our SPF guide. If you build and maintain records, bookmark this.<br>SPF Record Syntax at a Glance<br>An SPF record is one string of text in the RDATA of a single TXT record, and its grammar has exactly three kinds of parts: a version tag, mechanisms (each with an optional qualifier), and modifiers. The version tag must be exactly v=spf1 — a record starting v=spf10 is discarded, not partially matched (§4.5).

Every SPF record is the same three kinds of parts: a version tag, mechanisms with optional qualifiers, and modifiers. The grammar in brief, from §3 and §4.6.1:<br>PartFormRoleVersionv=spf1, always first, exactSelects the record (§4.5)Mechanism[qualifier]name[:arg][/cidr]Tested against the client IP; can match or not match (§4.6.2)Qualifier+ - ~ ? before mechanismThe result returned when its mechanism matches (§4.6.2)Modifiername=value, at most once eachExtra information; never matched (§6)

The three kinds of parts in SPF record syntax Terms are separated by spaces. Mechanism names are case-insensitive, and terms containing none of =, :, or / are mechanisms (§4.6.1). One syntax error anywhere invalidates the whole record: check_host() — the receiver’s evaluation routine, as the RFC names it — validates SPF syntax first and returns PermError immediately if anything is malformed, without evaluating a single term (§4.6). That is why one stray character can fail authentication for every message a domain sends.<br>SPF Mechanisms<br>Eight mechanisms exist, and each either matches the connecting IP or does not. RFC 7208 §5 splits them into basic framework mechanisms (all, include) and designated-sender mechanisms (a, mx, ptr, ip4, ip6, exists). Before the full table: most real-world records use only include, ip4, ip6, and all — the rest of this reference exists so you can read other people’s records, not because your own needs them.<br>MechanismSyntaxMatches when…Counts toward 10-lookup limit?RFC §allallAlwaysNo§5.1includeinclude:domainThe referenced record returns PassYes§5.2aa[:domain][/cidr]Client IP is among the domain’s A/AAAA addressesYes§5.3mxmx[:domain][/cidr]Client IP is an address of one of the domain’s MX hostsYes (plus per-MX address caps)§5.4ptrptr[:domain]Reverse DNS validates into the target domainYes§5.5ip4ip4:network[/cidr]Client IP is inside the IPv4 networkNo§5.6ip6ip6:network[/cidr]Client IP is inside the IPv6 networkNo§5.6existsexists:domainThe constructed domain has any A recordYes§5.7

All eight SPF mechanisms with syntax, match condition, and DNS lookup cost You can parse any published record term-by-term — with each mechanism’s cost and RFC cite attached — using the SPF syntax inspector.<br>all<br>all always matches, which is why it belongs at the end as the explicit default (§5.1). Everything after it is dead text: “Mechanisms listed after all MUST be ignored,” and any redirect= modifier is ignored whenever all appears anywhere in the record (§5.1). A record without a trailing all or redirect= silently defaults to Neutral (§4.7).<br>include<br>include:domain recursively evaluates the referenced domain’s SPF record and matches only when that evaluation returns Pass (§5.2). It is not a splice. The RFC itself concedes the name was “poorly chosen”: a -all inside an included record does not fail your outer record — a Fail, Softfail, or Neutral inside simply means “no match here, keep going” (§5.2). Better mental model: if-match. One consequence worth knowing: if the included domain publishes no SPF record at all, include returns PermError (§5.2).<br>a and mx<br>a matches when the client IP is one of the target domain’s A or AAAA addresses; mx matches when it is an address of one of the domain’s MX hosts (§5.3, §5.4). Both default to the current domain when no argument is given, and both accept dual CIDR suffixes — a/24 compares only the top 24 bits, and a:example.com/24//64 sets IPv4 and IPv6 prefixes separately (§5.3). Note the cost asymmetry: mx is one term against the 10-lookup limit, but evaluating it triggers one MX query plus an address query per MX...

record domain mechanisms syntax modifiers match

Related Articles