SPF Record Syntax: Mechanisms, Qualifiers, Modifiers, and Macros
SPF Record Syntax: Mechanisms, Qualifiers, Modifiers, and Macros
A Comprehensive Guide to RFC 7208
SPF (Sender Policy Framework) records are structured as a single DNS TXT entry. They must begin with the version identifier v=spf1, followed by a series of space-delimited terms. These terms consist of mechanisms (which may have qualifiers) and modifiers. The receiving mail server evaluates these terms from left to right, stopping at the first match.
Example of a Complete Record
v=spf1 ip4:192.0.2.0/24 include:_spf.example.com -all
In the example above, the domain authorizes a specific /24 subnet and a third-party service, while explicitly failing all other sources.
All governing rules are found in RFC 7208, published in April 2014. This reference covers every mechanism, qualifier, modifier, and macro, including evaluation logic and DNS constraints.
SPF Record Syntax at a Glance
An SPF record is essentially a string within the RDATA of a TXT record. Its grammar is composed of three distinct elements:
- Version Tag: Must be exactly
v=spf1. If a record starts with, it is ignored entirely (§4.5).v=spf10 - Mechanisms: These are tested against the connecting client's IP address to determine a match (§4.6.2).
- Modifiers: These provide additional metadata and are not used for matching (§6).
Grammar Summary (§3 & §4.6.1)
| Part | Format | Role | RFC Section |
|---|---|---|---|
| Version | v=spf1 | Mandatory start; identifies the record | §4.5 |
| Mechanism | [qualifier]name[:arg][/cidr] | Validates the client IP | §4.6.2 |
| Qualifier | +, -, ~, ? | Defines the result if the mechanism matches | §4.6.2 |
| Modifier | name=value | Extra info (max one of each type) | §6 |
Evaluation Logic
The receiver's evaluation routine, known as check_host(), follows a strict process:
- Terms are separated by spaces.
- Mechanism names are case-insensitive.
- Any term lacking
=,:, or/is treated as a mechanism (§4.6.1).
Warning: A single syntax error invalidates the entire record. If
check_host()detects a malformed term, it immediately returns aPermErrorwithout evaluating any other parts (§4.6). This is why a tiny typo can break email delivery for an entire organization.
SPF Mechanisms
There are eight mechanisms in total. RFC 7208 §5 categorizes them into Framework Mechanisms (all, include) and Designated-Sender Mechanisms (a, mx, ptr, ip4, ip6, exists).
Mechanism Reference Table
| Mechanism | Syntax | Match Condition | DNS Cost | RFC § |
|---|---|---|---|---|
all | all | Always matches | No | §5.1 |
include | include:domain | Referenced record returns Pass | Yes | §5.2 |
a | a[:domain][/cidr] | IP matches domain's A/AAAA records | Yes | §5.3 |
mx | mx[:domain][/cidr] | IP matches domain's MX hosts | Yes* | §5.4 |
ptr | ptr[:domain] | Reverse DNS matches target domain | Yes | §5.5 |
ip4 | ip4:network[/cidr] | IP is within the IPv4 range | No | §5.6 |
ip6 | ip6:network[/cidr] | IP is within the IPv6 range | No | §5.6 |
exists | exists:domain | Domain has any A record | Yes | §5.7 |
*MX mechanisms trigger one MX query and up to 10 address queries; exceeding this limit results in a PermError (§4.6.4).
Detailed Mechanism Breakdown
The all Mechanism
The all mechanism is a "catch-all" that always matches. Consequently, it must be placed at the end of the record (§5.1).
- Any terms appearing after
allare ignored. - If
allis present, anyredirect=modifier is ignored. - Records lacking
allorredirect=default to a Neutral result (§4.7).
The include Mechanism
This triggers a recursive check of another domain's SPF record. It matches only if that external record returns a Pass (§5.2).
Nuance: The RFC admits "include" is a misleading name. If an included record contains a
-all(Fail), it does not cause the primary record to fail. It simply means "no match here; continue to the next term in the primary record."
Critical Note: If the domain specified in an include has no SPF record, the result is a PermError (§5.2).
a and mx Mechanisms
a: Matches if the client IP is an A or AAAA address of the target domain (§5.3).mx: Matches if the client IP is an address of one of the target domain's MX hosts (§5.4).- Defaults: Both use the current domain if no argument is provided.
- CIDR Support: They support dual CIDR suffixes. For example,
a/24checks the first 24 bits, anda:example.com/24//64specifies different prefixes for IPv4 and IPv6.
ip4 and ip6 Mechanisms
These test if the client IP resides within a literal network using a colon (e.g., ip4:192.0.2.0/24).
- Incorrect Syntax:
ip4=192.0.2.0/24 - Defaults: Omitted CIDR lengths default to for IPv4 and for IPv6.
- Restriction: Truncated addresses (like
192.0.2) are forbidden (§5.6). - Efficiency: These are the most efficient mechanisms as they incur zero DNS cost.
The exists Mechanism
This mechanism constructs a domain name and performs an A record query. If any A record exists—regardless of the IP address—it matches (§5.7). This is always an A query, even for IPv6 connections.
When paired with macros (§7), exists allows for dynamic authorization. By publishing per-IP hostnames in a controlled zone, a domain can authorize specific IPs without listing them explicitly in the SPF record (a technique used by Salesforce).
SPF Validation Checklist
- Does the record start with
v=spf1? - Are all terms separated by single spaces?
- Is the
allmechanism placed at the very end? - Does the total number of DNS lookups stay ?
- Are
ip4andip6mechanisms using colons (:) rather than equals signs (=)?