TL;DR
SPF (Sender Policy Framework) is the first line of defense against email spoofing, allowing domain owners to declare which IP addresses and servers are authorized to send mail on their behalf. Defined in RFC 7208, SPF has a deceptively simple syntax but hides critical constraints โ most notably the 10 DNS lookup limit that silently breaks authentication for complex sending infrastructures. This guide covers every mechanism, qualifier, and modifier in the SPF specification, walks through real-world lookup counting, and explains how SPF interacts with DMARC alignment.
๐ Table of Contents
- How SPF Works
- SPF Result Codes
- Full Mechanism Reference
- The 10-Lookup Limit
- Real Lookup Count Examples
- Provider Include Directives
- SPF + DMARC Alignment
- SPF Flattening Techniques
- Best Practices
- Common Mistakes
- Tools
- Sources & References
1. How SPF Works
When a receiving mail server accepts an incoming SMTP connection, it extracts the domain from the MAIL FROM command (the envelope sender, also called the Return-Path) and performs a DNS TXT lookup on that domain. If an SPF record is found, the receiver evaluates the connecting IP against the authorized mechanisms listed in the record.
๐ Definition โ SPF (Sender Policy Framework) is a DNS-based email authentication protocol (RFC 7208) that allows a domain owner to publish a list of IP addresses and hostnames authorized to send email using that domain as the envelope sender.
# SPF evaluation flow
1. Sender connects from IP 198.51.100.42
2. SMTP MAIL FROM: user@example.com
3. Receiver queries TXT record for example.com
4. Record: "v=spf1 ip4:198.51.100.0/24 include:_spf.google.com -all"
5. 198.51.100.42 matches ip4:198.51.100.0/24 โ Result: PASS
โ ๏ธ SPF does NOT validate the From: header that users see in their mail client. It validates the MAIL FROM (envelope sender / Return-Path). This is why DMARC alignment is required to connect SPF results to the visible sender identity.
2. SPF Result Codes
RFC 7208 ยง2.6 defines seven possible evaluation results:
| Result | Qualifier | Meaning | Typical Action |
|---|---|---|---|
| Pass |
+ (default) |
IP is explicitly authorized | Accept |
| Fail | - |
IP is explicitly not authorized | Reject or mark as spam |
| SoftFail | ~ |
IP is probably not authorized (transitional) | Accept but flag |
| Neutral | ? |
Domain makes no assertion about this IP | Accept |
| None | โ | No SPF record found | Accept (no policy) |
| PermError | โ | Permanent error (syntax error, >10 lookups) | Treat as fail or neutral (receiver-dependent) |
| TempError | โ | Temporary DNS error | Try again later (4xx) |
๐ซ PermError is silent and catastrophic. If your SPF record exceeds 10 DNS lookups, receivers return PermError โ which many treat as a fail. Your legitimate mail gets blocked or junked, and you receive no bounce notification.
3. Full Mechanism Reference
| Mechanism | DNS Lookups | Syntax | Description |
|---|---|---|---|
ip4 |
0 | ip4:198.51.100.0/24 |
Match IPv4 address or CIDR range |
ip6 |
0 | ip6:2001:db8::/32 |
Match IPv6 address or CIDR range |
a |
1 |
a or a:mail.example.com
|
Match the A/AAAA record of the domain |
mx |
1 + MX lookups |
mx or mx:example.com
|
Match the MX hosts' IPs |
include |
1 + nested | include:_spf.google.com |
Evaluate another domain's SPF record recursively |
exists |
1 | exists:%{i}.bl.example.com |
Pass if A record exists for macro-expanded domain |
all |
0 |
-all / ~all
|
Catch-all โ always matches (must be last) |
redirect |
1 | redirect=_spf.example.com |
Replace entire record with another domain's SPF |
exp |
1 | exp=explain._spf.example.com |
Custom explanation string for failures |
๐ก The ptr mechanism exists in the spec but RFC 7208 ยง5.5 explicitly discourages its use: "This mechanism SHOULD NOT be published." It is slow, unreliable, and counts as a DNS lookup.
4. The 10 DNS Lookup Limit
RFC 7208 ยง4.6.4 imposes a hard limit: SPF evaluation must not cause more than 10 DNS mechanisms/modifiers that require lookups. This includes include, a, mx, exists, redirect, and ptr. It does not include ip4, ip6, or all.
# These count toward the 10-lookup limit:
include: โ 1 lookup (+ any nested lookups from the included record)
a โ 1 lookup
mx โ 1 lookup (+ additional A lookups for each MX host)
exists โ 1 lookup
redirect โ 1 lookup
ptr โ 1 lookup (deprecated)
# These do NOT count:
ip4: โ 0 lookups (direct IP comparison)
ip6: โ 0 lookups (direct IP comparison)
all โ 0 lookups (catch-all terminator)
10maximum DNS lookups allowed per SPF evaluation (RFC 7208 ยง4.6.4)
โ ๏ธ The limit is recursive. If include:_spf.google.com itself contains 3 include: directives, those 3 count toward your total of 10.
Void Lookup Limit
RFC 7208 ยง4.6.4 also defines a void lookup limit of 2. A void lookup is a DNS query that returns either NXDOMAIN or an empty answer. If more than 2 void lookups occur during evaluation, the result is PermError.
5. Real Lookup Count Examples
Here is a real-world example of how lookups accumulate for a domain using Google Workspace, Mailchimp, and SendGrid:
v=spf1 include:_spf.google.com include:servers.mcsv.net include:sendgrid.net -all
Lookup count breakdown:
โโ include:_spf.google.com โ 1
โ โโ include:_netblocks.google.com โ 2
โ โโ include:_netblocks2.google.com โ 3
โ โโ include:_netblocks3.google.com โ 4
โโ include:servers.mcsv.net โ 5
โ โโ include:mcsv.net โ 6
โโ include:sendgrid.net โ 7
โ โโ include:u12345.wl.sendgrid.net โ 8
โโ Total: 8 lookups โ
(under 10)
๐ซ Now add Salesforce (include:_spf.salesforce.com โ 1) and Zendesk (include:mail.zendesk.com โ 2 nested). That brings you to 11 lookups โ PermError. Your email breaks silently.
6. Provider Include Directives
| Provider | Include Directive | DNS Lookups Used |
|---|---|---|
| Google Workspace | include:_spf.google.com |
~4 |
| Microsoft 365 | include:spf.protection.outlook.com |
~2 |
| SendGrid | include:sendgrid.net |
~2 |
| Mailchimp | include:servers.mcsv.net |
~2 |
| Amazon SES | include:amazonses.com |
~1 |
| Salesforce | include:_spf.salesforce.com |
~1 |
| HubSpot | include:spf.hubspot.com |
~2 |
| Zendesk | include:mail.zendesk.com |
~3 |
| Freshdesk | include:email.freshdesk.com |
~2 |
โก Pro Tip: Before adding a new include:, always check its nested lookup count with an SPF checker tool. A single include can consume 1โ5 lookups depending on the provider's own SPF chain.
7. SPF + DMARC Alignment
For DMARC to consider SPF as a passing authentication method, two conditions must be met:
SPF must evaluate to Pass for the
MAIL FROMdomain.The
MAIL FROMdomain must align with theFrom:header domain (relaxed or strict per DMARC'saspftag).
โ ๏ธ Most third-party senders use their own Return-Path (e.g., bounces.sendgrid.net), which means SPF passes for their domain, not yours. SPF alignment fails โ DMARC must rely on DKIM alignment instead.
Custom Return-Path
To achieve SPF alignment with third-party senders, configure a custom return-path using your subdomain:
# Instead of:
Return-Path: bounces@em.sendgrid.net โ SPF passes for sendgrid.net (not aligned)
# Configure:
Return-Path: bounces@em.yourdomain.com โ SPF passes for yourdomain.com (aligned!)
# DNS: Add CNAME
em.yourdomain.com โ u12345.wl.sendgrid.net
8. SPF Flattening Techniques
When you hit the 10-lookup limit, SPF flattening replaces include: directives with their resolved ip4: and ip6: addresses, reducing lookups to zero for those entries.
# Before flattening (8 lookups):
v=spf1 include:_spf.google.com include:servers.mcsv.net include:sendgrid.net -all
# After flattening (0 lookups for Google IPs):
v=spf1 ip4:209.85.128.0/17 ip4:172.217.0.0/16 ip4:108.177.8.0/21
include:servers.mcsv.net include:sendgrid.net -all
๐ซ Flattening is fragile. Provider IPs change without notice. If Google adds a new IP range and your flattened record doesn't include it, mail from that IP fails SPF. You must automate flattening with a tool that re-resolves and updates your DNS record regularly.
๐ฏ A safer alternative to flattening is using the redirect= modifier to point to a dynamically managed SPF record on a subdomain, or using a service like Cloudflare's SPF management that handles flattening automatically.
9. Best Practices
End with -all
Use a hard fail (-all) in production. ~all (softfail) is for testing only; it lets unauthorized mail through.
Monitor lookup count
Check your total DNS lookup count after every change. A single vendor addition can push you over 10.
One record per domain
RFC 7208 ยง3.2: a domain must not have multiple SPF records. Two records cause PermError.
Avoid deprecated mechanisms
Never use ptr. It's slow, unreliable, and wastes a lookup. RFC 7208 explicitly discourages it.
Use ip4/ip6 for static IPs
Dedicated servers with fixed IPs should use ip4:/ip6: โ no lookup cost.
Keep under 255 characters per string
DNS TXT records are limited to 255-byte strings. Long records must be split into multiple strings within a single TXT record set.
10. Common Mistakes
๐ซ Multiple SPF records on the same domain. Publishing two TXT records that start with v=spf1 causes PermError (RFC 7208 ยง3.2). Merge them into one record.
๐ซ Exceeding the 10-lookup limit. This is the #1 SPF misconfiguration. Every include: adds lookups recursively. Use an SPF checker to validate the total.
โ ๏ธ Using +all. A record ending with +all authorizes the entire internet to send as your domain. This is equivalent to having no SPF record at all.
โ ๏ธ Forgetting the v=spf1 prefix. Without the version tag, the TXT record is not recognized as SPF. Receivers will return "None" (no SPF found).
โ ๏ธ Confusing include with redirect. include: evaluates another record and returns its result. redirect= replaces the entire evaluation with another domain's record. redirect cannot coexist with all.
โ ๏ธ Placing mechanisms after all. The all mechanism matches everything โ anything after it is never evaluated. Always put all last.
11. Tools
Validate and build your SPF records with these tools:
| Tool | Purpose |
|---|---|
| SPF Record Checker | Validate syntax, count DNS lookups, detect errors |
| SPF Record Generator | Build a syntactically correct SPF record by selecting providers |
12. Sources & References
๐ RFC 7208 โ Sender Policy Framework (SPF) for Authorizing Use of Domains in Email
๐ RFC 7489 โ DMARC
๐ Google Workspace โ Authorize email senders with SPF
๐ Microsoft 365 โ Set up SPF to help prevent spoofing
๐ Cloudflare โ What is an SPF record?
๐ M3AAWG โ Best Practices for Implementing DMARC (includes SPF guidance)
๐ฏ Key Takeaway: SPF tells the world which IPs may send email as your domain โ but it only validates the envelope sender, not the visible From: header. Respect the 10 DNS lookup limit religiously, always end with -all, and pair SPF with DKIM so DMARC has a reliable alignment path that survives forwarding. Check your lookup count after every change.
Originally published on StarNomina ToolBox. Try our free online tools โ no signup required.
Top comments (0)