Back to blog
Authentication··5 min read·WillItInbox Team

SPF record syntax: a complete cheat sheet (with examples)

Every SPF mechanism, qualifier, and modifier — what they mean, when to use them, and the four mistakes that break 90% of records.

SPFAuthenticationDNS

Sender Policy Framework records are deceptively simple — one TXT record at your domain root listing who's allowed to send. In practice, the syntax has enough quirks that the majority of records we see in WillItInbox reports are subtly broken. This is the cheat sheet we wish every email admin had taped to their monitor.

Anatomy of an SPF record

Every SPF record follows the same skeleton: a version tag, one or more mechanisms with optional qualifiers, and an all mechanism at the end. Here's a complete record with annotations.

example.com SPF recordtxt
v=spf1 ip4:192.0.2.0/24 include:_spf.google.com include:sendgrid.net ~all
  • `v=spf1` — the version tag. Always required, always exactly this string.
  • `ip4:192.0.2.0/24` — authorize a specific IPv4 range.
  • `include:_spf.google.com` — defer to Google's SPF record. Counts as one DNS lookup.
  • `include:sendgrid.net` — same, for SendGrid.
  • `~all` — softfail anything that doesn't match (allow, but mark suspicious).

Every mechanism, in plain English

MechanismMatchesDNS lookups
allAlways matches (use last with a qualifier)0
ip4:<addr>An IPv4 address or CIDR range0
ip6:<addr>An IPv6 address or CIDR range0
a / a:<domain>The A record(s) of the domain1
mx / mx:<domain>The MX hosts of the domain1 (plus 1 per MX)
include:<domain>The included domain's SPF record1 (plus its lookups)
exists:<domain>True if the domain resolves at all1
ptr / ptr:<domain>Reverse DNS of the connecting IP. Deprecated.Many
SPF mechanisms — what they match and how many DNS lookups each costs.

Qualifiers — the symbol before each mechanism

  • `+` (Pass) — implicit default. include:foo is the same as +include:foo.
  • `~` (SoftFail) — receivers should accept but mark as suspicious. Common on ~all.
  • `-` (Fail) — receivers should reject. Use on -all once you're confident.
  • `?` (Neutral) — no opinion. Useless in practice.

The 10-DNS-lookup limit

RFC 7208 §4.6.4 caps the total number of DNS lookups during SPF evaluation at 10. Exceed this and the result is permerror — which most receivers treat as a hard fail. Lookup-counting mechanisms include include, a, mx, exists, ptr, and redirect=. Nested includes count too: if include:_spf.salesforce.com itself contains five includes, you've used six lookups before doing anything else.

Three reliable ways to stay under the cap:

  1. Remove unused providers. That ESP from three years ago doesn't need to be in the record. Audit annually.
  2. Replace `include:` with `ip4:`. If a provider publishes a stable IP list, paste the IPs directly. You trade one include lookup for zero lookups, at the cost of having to update when the provider changes IPs (rare for well-run ESPs).
  3. Use SPF flattening. Tools like spf-flatten or scl-spf rewrite your record at publish time, expanding all includes to literal IPs. Schedule a monthly re-flatten so provider changes are picked up.

The `redirect=` modifier

If you operate many domains that share one SPF policy, redirect= lets the others delegate. The redirect target's SPF replaces yours entirely — your all is ignored.

txt
v=spf1 redirect=_spf.example.com

Useful for keeping a single source of truth across, say, example.com and example.co.uk. Not to be combined with mechanisms in the same record — the redirect overrides them.

The `exp=` modifier

An optional explanation string returned to senders when SPF fails. Almost no one uses it because almost no one reads bounce messages. You can safely ignore it.

Real-world example records

Small business — only Google Workspace

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

Marketing + transactional split

txt
v=spf1 include:_spf.google.com include:mailgun.org include:sendgrid.net ~all

Self-hosted with a single fixed IP

txt
v=spf1 ip4:203.0.113.42 a:mail.example.com -all

The four mistakes that break 90% of records

  1. Multiple SPF records. A second v=spf1 record at the same domain causes permerror. Merge into one.
  2. Exceeding 10 lookups. Same permerror result. Audit and flatten.
  3. Trailing `+all` or `?all`. Marketing tools that auto-generate records sometimes do this. Always check the final mechanism.
  4. Stale providers. A 2-year-old provider still in your record is an unmaintained attack surface. Audit annually.

Publishing changes safely

  1. Lower the existing record's TTL to 300 (5 minutes) at least 24 hours before changes.
  2. Make the change. Verify with dig txt yourdomain.com from a few resolvers.
  3. Send a test through WillItInbox. Confirm the change is reflected.
  4. Wait 48 hours. Watch DMARC aggregate reports for SPF failures.
  5. Restore the TTL to 3600 once stable.

Frequently asked questions

Last updated April 26, 2026.

Keep reading