A complete reference to SPF (Sender Policy Framework) record syntax. Learn every mechanism, qualifier, and modifier — plus how to stay within the 10 DNS lookup limit.

SPF Record Structure

An SPF record is a DNS TXT record on your domain that lists the servers authorised to send email on your behalf. Every SPF record follows this structure:

v=spf1 [mechanisms] [modifiers] [all]
Key rules:
  • Must start with v=spf1 (exactly, case-sensitive)
  • Only ONE SPF record per domain (multiple records = permanent error)
  • Maximum 255 characters per DNS TXT string (but can be split into multiple strings)
  • Total record should stay under 512 bytes for UDP compatibility
  • Mechanisms are evaluated left to right — first match wins

Qualifiers

Every mechanism can be prefixed with a qualifier that determines what happens when it matches:

Qualifier Meaning Result Usage
+ Pass Allow the email Default if no qualifier specified. ip4:1.2.3.4 = +ip4:1.2.3.4
- Fail (hard) Reject the email Most commonly used on -all at the end of a record
~ SoftFail Accept but mark suspicious Commonly used during transition: ~all. With DMARC, treated the same as fail.
? Neutral No opinion Rarely used. Equivalent to having no SPF record for that mechanism.
💡 With DMARC: Both -all and ~all result in an SPF failure for DMARC alignment purposes. The distinction mainly matters for servers that don’t implement DMARC.

Mechanisms

ip4 and ip6 — IP Address Ranges

Match by the sender’s IP address. Does NOT count toward the 10 DNS lookup limit.

ip4:203.0.113.5 # Single IPv4 address
ip4:203.0.113.0/24 # IPv4 CIDR range (256 addresses)
ip6:2001:db8::1 # Single IPv6 address
ip6:2001:db8::/32 # IPv6 CIDR range

a — A/AAAA Record Lookup

Matches if the sender’s IP matches the A (or AAAA) record of the specified domain. Counts as 1 DNS lookup.

a # Check the current domain’s A record
a:mail.example.com # Check a specific domain’s A record
a:example.com/24 # Match the /24 range around the A record IP

mx — MX Record Lookup

Matches if the sender’s IP matches any of the domain’s MX record addresses. Counts as 1 DNS lookup (plus 1 for each MX hostname resolved).

mx # Check current domain’s MX records
mx:example.com # Check another domain’s MX records
mx/24 # Match the /24 range around MX IPs

include — Include Another Domain’s SPF

Evaluates the SPF record of another domain. If that record produces a “pass”, this mechanism matches. Counts as 1 DNS lookup (plus any lookups within the included record).

include:_spf.google.com # Google Workspace
include:spf.protection.outlook.com # Microsoft 365
include:sendgrid.net # SendGrid
include:servers.mcsv.net # Mailchimp
⚠️ Warning: Each include triggers recursive lookups. include:_spf.google.com alone uses 3–4 lookups. This is the most common cause of exceeding the 10-lookup limit.

exists — Domain Existence Check

Passes if an A record exists for the specified domain (any IP). Used for advanced macro-based scenarios. Counts as 1 DNS lookup.

exists:%{i}.spf.example.com # Macro: check if sender IP has an A record

all — Catch-All

Matches everything. Always placed at the end of the record as the default rule.

-all # Hard fail: reject anything not explicitly allowed (recommended)
~all # Soft fail: mark as suspicious but accept (common during transition)
?all # Neutral: no opinion on unmatched senders (rarely useful)
+all # Pass everything: defeats the purpose of SPF (never use this)

Modifiers

redirect

Replaces the entire SPF evaluation with another domain’s SPF record. Unlike include, it’s a full replacement — there’s no fallthrough. Counts as 1 lookup.

v=spf1 redirect=_spf.example.com

Only takes effect if no mechanisms in the record match. Cannot be used with all.

exp — Explanation

Specifies a domain whose TXT record contains a human-readable explanation for SPF failures. Rarely used in practice.

v=spf1 ip4:203.0.113.0/24 -all exp=explain.example.com

The 10 DNS Lookup Limit

🚨 This Is the #1 SPF Problem
RFC 7208 limits SPF evaluation to 10 DNS lookups. If your record exceeds this limit, the entire SPF check returns a permanent error (PermError) — which means SPF effectively doesn’t work at all.

What Counts as a DNS Lookup

❌ Counts (uses a lookup)
  • include:
  • a / a:
  • mx / mx:
  • exists:
  • redirect=
  • Nested lookups within include:
✓ Does NOT count
  • ip4:
  • ip6:
  • all
  • The initial SPF record lookup itself

Example: Counting Lookups

v=spf1
include:_spf.google.com # 1 + ~3 nested = 4 lookups
include:spf.protection.outlook.com # 1 + ~2 nested = 3 lookups
include:sendgrid.net # 1 + ~1 nested = 2 lookups
ip4:203.0.113.5 # 0 lookups (IP-based)
a # 1 lookup
-all
# Total: ~10 lookups — right at the limit!

SPF Flattening

If you’re hitting the 10-lookup limit, SPF flattening replaces include: mechanisms with the actual IP addresses they resolve to. This eliminates the DNS lookups but requires regular updates when providers change their IP ranges.

⚠️ Flattening trade-offs:
  • IPs change without notice — you need automated re-flattening (weekly minimum)
  • Records become much longer (many IP ranges)
  • Harder to read and debug
  • Consider SPF flattening services that automate this

Common Provider SPF Includes

Provider SPF Include ~Lookups Used
Google Workspace include:_spf.google.com 3–4
Microsoft 365 include:spf.protection.outlook.com 2–3
SendGrid include:sendgrid.net 1–2
Mailchimp include:servers.mcsv.net 1–2
Amazon SES include:amazonses.com 1–2
Zoho Mail include:zoho.com 2–3
Postmark include:spf.mtasv.net 1
Mailgun include:mailgun.org 1–2
HubSpot include:spf.hubspot.com 1

Example SPF Records

Simple: Google Workspace Only

v=spf1 include:_spf.google.com -all

Common: Google + Marketing Platform

v=spf1 include:_spf.google.com include:sendgrid.net -all

Complex: Multiple Services + Own Server

v=spf1 ip4:203.0.113.5 include:_spf.google.com include:servers.mcsv.net include:spf.mtasv.net -all

Domain That Doesn’t Send Email

v=spf1 -all

Explicitly says “no server is authorised to send email for this domain.” Important for protecting parked domains.

Best Practices

  • Use ip4:/ip6: where possible to conserve DNS lookups
  • End with -all (hard fail) once you’ve confirmed all legitimate senders
  • Use ~all (soft fail) during the transition period
  • Regularly audit your SPF record — remove old providers you no longer use
  • Keep only ONE SPF record per domain
  • Publish SPF records on all domains and subdomains — even those that don’t send email
  • Use our Domain Checker to verify your SPF record and count lookups

For the bigger picture on how SPF fits with DKIM and DMARC, see SPF vs DKIM vs DMARC. For step-by-step setup, see What is SPF?.