SPF Technical Reference (RFC 7208)

A section-by-section walkthrough of RFC 7208, the standard that defines SPF. Covers every mechanism, qualifier, the 10-lookup limit, void lookups, ptr deprecation, macros, and security considerations — with practical examples and tool links.

11 min readstandardsThomas Johnson

What RFC 7208 Defines

RFC 7208 is the authoritative specification for the Sender Policy Framework (SPF). Published in April 2014 by the IETF, it supersedes RFC 4408 and defines how domain owners declare which IP addresses are authorized to send email on their behalf, and how receiving mail servers evaluate those declarations.

Every SPF check your email goes through — at Gmail, Microsoft 365, Yahoo, or any other receiver — follows the rules in this document. When our SPF Checker reports a PermError, a lookup count, or a deprecated mechanism, the source is RFC 7208.

This reference walks through the sections that matter to email administrators, with the RFC's own language quoted where it clarifies the rule, followed by practical explanations and links to mxio tools and fix-it guides.


§3.1 — The v=spf1 Prefix and DNS Record Type

"An SPF record is a DNS Resource Record (RR) of type TXT [...] An SPF record begins with a version tag of 'v=spf1'."

Every SPF record is published as a TXT record and must begin with exactly v=spf1. The prefix is case-insensitive (V=spf1 and v=SPF1 are equivalent), but lowercase is conventional. Records that begin with any other version string are ignored by SPF evaluation.

The RFC originally defined a dedicated SPF DNS record type (type 99), but it was never widely adopted. §3.1 states that implementations "MUST NOT use type SPF" and must use TXT records exclusively. If you see an SPF-type record in your DNS, it has no effect.

Check it: The mxio SPF Checker validates the version prefix and warns if no valid v=spf1 record is found.


§3.2 — The One-Record Rule

"A domain name MUST NOT have multiple records that would cause an authorization check to select more than one record."

A domain may have exactly one SPF record. If two or more TXT records begin with v=spf1, the evaluation produces a PermError — a permanent failure applied to every message, regardless of whether the sending IP appears in either record.

This is the single most common SPF misconfiguration. It happens when an administrator adds a new SPF record (for a SaaS service, a marketing platform, or a migration) without merging it into the existing one.

Fix it: See Multiple SPF Records on One Domain for the step-by-step merge process.

Build it right: Use the mxio SPF Builder to generate a single, correct record from your selected providers.


§3.4 — Record Evaluation Order

SPF mechanisms are evaluated left to right. The first mechanism that matches the sending IP determines the result. Evaluation stops immediately — no further mechanisms are checked.

This has practical consequences for record optimization:

  • Put high-volume senders first. If 90% of your email flows through Google Workspace, include:_spf.google.com should appear before less-used includes. The sooner a match is found, the fewer DNS lookups the evaluator performs.
  • Order does not affect the lookup count. All include, a, mx, redirect, and exists mechanisms count against the 10-lookup limit regardless of evaluation order. But placing frequent matches early reduces the actual DNS queries made per message.

§4.5 — Qualifiers: +, -, ~, ?

Every mechanism has a qualifier that determines the SPF result when that mechanism matches. If no qualifier is written, + (Pass) is implied.

Qualifier Result Meaning
+ Pass The sender is authorized
- Fail The sender is not authorized — reject
~ SoftFail The sender is probably not authorized — accept but mark
? Neutral The domain makes no assertion about this sender

The qualifier that matters most is the one on your all mechanism — the catch-all at the end of the record. -all tells receivers to reject unauthorized senders. ~all tells them to accept but treat with suspicion.

When to use which: See SPF ~all vs -all: Softfail vs Hardfail for the tradeoffs, including how each interacts with DMARC and forwarding.


§4.6.4 — The 10-Lookup Limit

"SPF implementations MUST limit the number of mechanisms and modifiers that do DNS lookups to at most 10 per SPF check."

This is the constraint that drives the entire SPF flattening industry. RFC 7208 caps the total number of DNS-querying mechanisms at 10. Mechanisms that count include:

Mechanism Lookup Cost
include: 1 (plus the lookups in the included record, recursively)
a / a: 1
mx / mx: 1 (plus additional A lookups for each MX host)
redirect= 1
exists: 1
ptr / ptr: 1

Mechanisms that do not consume lookups:

Mechanism Lookup Cost
ip4: 0 — direct CIDR match
ip6: 0 — direct CIDR match
all 0 — always matches, no DNS query

When the limit is exceeded, the evaluator returns PermError — a permanent failure. Every email from every IP is treated as failing SPF, regardless of whether the sender is listed in the record. This is not a warning. It is a hard failure.

Check it: Run the mxio SPF Checker to see your exact lookup count, broken down by mechanism.

Fix it: See Fix SPF PermError: Too Many DNS Lookups for reduction strategies, or learn about SPF flattening to resolve includes into IP addresses automatically.

The 2 Void-Lookup Limit

The same section imposes a lesser-known constraint:

"SPF implementations SHOULD limit the number of 'void lookups' to two."

A void lookup is a DNS query that returns either NXDOMAIN (the name does not exist) or an empty answer (the name exists but has no records of the queried type). If more than 2 void lookups occur during evaluation, the implementation "SHOULD" (strong recommendation, per RFC 2119) return a PermError.

This catches stale include mechanisms that point to domains that no longer publish SPF records. If you have removed a sending service but left its include: in your SPF record, and that domain's SPF TXT record has been deleted, every query against it produces a void lookup.

The mxio SPF Checker reports your void lookup count alongside the main lookup budget.


§5 — Mechanisms

§5.1 — all

The catch-all mechanism matches every sender. It must appear last in the record — any mechanisms after all are never evaluated. The qualifier on all sets the default policy:

  • -all — strict: reject anyone not explicitly authorized
  • ~all — permissive: accept but mark unauthorized senders
  • +all — open: authorize everyone (defeats the purpose of SPF entirely)

A record without an all mechanism defaults to ?all (Neutral), which provides no protection.

§5.2 — include:

"If the 'include' target does not have a valid SPF record, this results in a 'PermError'."

The include mechanism delegates evaluation to another domain's SPF record. It consumes 1 lookup, plus all lookups within the included record (recursively). This is where lookup counts grow fast — a single include:_spf.google.com can consume 3-4 lookups by the time Google's nested includes are resolved.

If the included domain has no SPF record, or if its record is malformed, the entire evaluation returns PermError — not just the include, but the whole check.

§5.3 — a and a:

Matches the A and AAAA records of the specified domain (or the current domain if no argument is given). Consumes 1 lookup. Useful when your web server also sends email.

§5.4 — mx and mx:

Matches the IP addresses of the domain's MX hosts. Consumes 1 lookup for the MX query, plus additional A/AAAA lookups for each MX hostname. The RFC limits the total MX names to 10 (separate from the 10-mechanism lookup limit) to prevent abuse.

§5.5 — ptr (Deprecated)

"This mechanism SHOULD NOT be used."

The RFC explicitly deprecates ptr. The mechanism performs a reverse DNS lookup on the sending IP, then a forward lookup on each returned hostname to verify the IP matches. This is slow (multiple round-trips), unreliable (many IPs lack PTR records or have misconfigured reverse DNS), and adds load to DNS infrastructure.

The mxio SPF Checker flags ptr mechanisms with a deprecation warning. If you have ptr in your record, replace it with explicit ip4: or ip6: ranges, or use a: to match the hostname directly.

§5.6–5.7 — ip4: and ip6:

Direct CIDR matching against IPv4 and IPv6 addresses. These mechanisms consume zero DNS lookups — the match is performed locally against the sending IP. This is why SPF flattening works: by resolving include mechanisms into ip4 and ip6 ranges, the lookup count drops to zero for those entries.

Syntax examples:

  • ip4:192.0.2.1 — single address
  • ip4:192.0.2.0/24 — CIDR range
  • ip6:2001:db8::/32 — IPv6 prefix

§5.8 — exists:

A specialized mechanism that passes if a DNS A lookup for the given domain returns any result. Used primarily with macros (§8) for complex per-sender policies. Most organizations never need exists.


§6.1 — The redirect= Modifier

"If all mechanisms fail to match, and a 'redirect' modifier is present, [...] the check_host() function is evaluated with the domain from the redirect."

The redirect modifier replaces the current policy entirely with another domain's SPF record. Unlike include (which only delegates a portion of the check), redirect defers the entire evaluation. It is useful for organizations that maintain a single canonical SPF record and want all their domains to reference it.

redirect is only evaluated if no mechanisms match — so if an all mechanism is present, redirect is never reached. This is a common misconfiguration: having both ~all and redirect= in the same record. The ~all catches everything, making the redirect dead code.


§8 — Macros

RFC 7208 defines a macro language that allows SPF records to include dynamic values derived from the SMTP transaction: %{s} (sender), %{l} (local-part), %{d} (domain), %{i} (sending IP), and others.

Macros enable per-sender or per-IP policy lookups without maintaining a monolithic record. For example, exists:%{i}._spf.example.com checks whether a specific sending IP has a corresponding DNS entry — effectively creating a dynamic whitelist.

In practice, macros are rare outside enterprise deployments and anti-spam services. Most organizations use standard include, ip4, and ip6 mechanisms. If you encounter macros in an SPF record, the mxio SPF Checker parses and evaluates them correctly.


§10 — Security Considerations

The RFC's security section contains several important caveats that every email administrator should understand:

SPF Does Not Authenticate the From: Header

SPF authenticates the envelope sender (the MAIL FROM / Return-Path address), not the From: header that the recipient sees in their email client. An attacker can set a valid SPF Return-Path of attacker@evil.com while displaying ceo@yourcompany.com in the From: header.

This is exactly the gap that DMARC fills. DMARC requires that the domain authenticated by SPF (or DKIM) aligns with the From: header domain. Without DMARC, SPF alone does not prevent display-name spoofing.

For a complete explanation of how SPF, DKIM, and DMARC work together, see the Email Authentication Guide.

SPF Breaks on Forwarding

When an email is forwarded, the forwarding server's IP is not in the original sender's SPF record. SPF fails. This is inherent to the protocol's design — SPF checks the immediate connecting server's IP, not the original sender.

This is why DKIM and ARC (RFC 8617) are essential complements to SPF. DKIM signatures travel with the message and survive forwarding (if the body is not modified). ARC preserves the original authentication results across forwarding hops.


Quick Reference: Tool Checks and RFC Sections

What the SPF Checker Reports RFC 7208 Section
Missing or invalid v=spf1 prefix §3.1
Multiple SPF records (PermError) §3.2
Lookup count over 10 (PermError) §4.6.4
Void lookup count over 2 §4.6.4
+all or no all policy §4.5, §5.1
ptr mechanism (deprecated) §5.5
Include chain errors §5.2
Qualifier meanings (+, -, ~, ?) §4.5

Full RFC Citation

Kitterman, S., "Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1", RFC 7208, DOI 10.17487/RFC7208, April 2014.

Excerpts from RFC 7208 are Copyright © 2014 IETF Trust and the persons identified as the document authors. All rights reserved. The full text of the RFC is available at rfc-editor.org.

Was this article helpful?

Related Articles