SPF record syntax: mechanisms, examples, and lookup limits
Use practical SPF examples to understand mechanisms, qualifiers, includes, redirects, DNS lookup limits, and common failure modes.
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. Use the free SPF checker when you want to inspect the currently published record before editing DNS.
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
| Mechanism | Matches | DNS lookups |
|---|---|---|
all | Always matches (use last with a qualifier) | 0 |
ip4:<addr> | An IPv4 address or CIDR range | 0 |
ip6:<addr> | An IPv6 address or CIDR range | 0 |
a / a:<domain> | The A record(s) of the domain | 1 |
mx / mx:<domain> | The MX hosts of the domain | 1 (plus 1 per MX) |
include:<domain> | The included domain's SPF record | 1 (plus its lookups) |
exists:<domain> | True if the domain resolves at all | 1 |
ptr / ptr:<domain> | Reverse DNS of the connecting IP. Deprecated. | Many |
Qualifiers — the symbol before each mechanism
- `+` (Pass) — implicit default.
include:foois the same as+include:foo. - `~` (SoftFail) — receivers should accept but mark as suspicious. Common on
~all. - `-` (Fail) — receivers should reject. Use on
-allonce 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:
- Remove unused providers. That ESP from three years ago doesn't need to be in the record. Audit annually.
- Replace `include:` with `ip4:`. If a provider publishes a stable IP list, paste the IPs directly. You trade one
includelookup for zero lookups, at the cost of having to update when the provider changes IPs (rare for well-run ESPs). - 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.
v=spf1 redirect=_spf.example.comUseful 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
v=spf1 include:_spf.google.com -allMarketing + transactional split
v=spf1 include:_spf.google.com include:mailgun.org include:sendgrid.net ~allSelf-hosted with a single fixed IP
v=spf1 ip4:203.0.113.42 a:mail.example.com -allThe four mistakes that break 90% of records
- Multiple SPF records. A second
v=spf1record at the same domain causespermerror. Merge into one. - Exceeding 10 lookups. Same
permerrorresult. Audit and flatten. - Trailing `+all` or `?all`. Marketing tools that auto-generate records sometimes do this. Always check the final mechanism.
- Stale providers. A 2-year-old provider still in your record is an unmaintained attack surface. Audit annually.
Publishing changes safely
- Lower the existing record's TTL to 300 (5 minutes) at least 24 hours before changes.
- Make the change. Verify with
dig txt yourdomain.comfrom a few resolvers. - Send a test through WillItInbox. Confirm the change is reflected.
- Wait 48 hours. Watch DMARC aggregate reports for SPF failures.
- Restore the TTL to 3600 once stable.
Frequently asked questions
After editing a record, use the free SPF checker to expand includes and count lookup-producing mechanisms. Pair the result with domain monitoring when third-party includes can change over time.
Continue this dmarc monitoring and sender authentication workflow with the commercial page, the core guide, the implementation docs.
Last updated June 13, 2026.
Sources reviewed
- RFC 7208: Sender Policy Framework(standard)
Factual review: June 13, 2026 by WillItInbox Editorial.
Keep reading