A complete guide to writing and reading SPF records — the settings in your domain’s public directory (DNS) that tell the world which servers are allowed to send email on your behalf. This page covers every part of an SPF record, what each piece does, and how to avoid the most common pitfalls. (Full name: Sender Policy Framework.)
What an SPF Record Looks Like
An SPF record is a text entry in your domain’s settings (a DNS TXT record) that lists who’s allowed to send email as your domain. Every SPF record follows this pattern:
- Must start with
v=spf1(exactly — it’s case-sensitive) - You can only have one SPF record per domain (multiple records cause errors)
- The record has a maximum length of 255 characters per string (but can be split into multiple strings)
- Keep the total under 512 bytes for best compatibility
- Entries are checked left to right — the first match wins
The Decision Prefixes (Qualifiers)
Each entry in your SPF record can have a prefix that says what to do when a sending server matches that entry. Think of it as the instruction: “if this matches, then…”
| Prefix | What It Means | What Happens | When to Use It |
|---|---|---|---|
+ |
Allow | The email is accepted | This is the default — you don’t even need to write it. ip4:1.2.3.4 means the same as +ip4:1.2.3.4 |
- |
Reject (hard fail) | The email should be rejected | Most commonly used at the end: -all means “reject everything not on my list” |
~ |
Suspicious (soft fail) | Accept the email but flag it as suspicious | Common during setup: ~all. When DMARC is enabled, soft fail and hard fail are treated the same. |
? |
No opinion | Neither allow nor reject | Rarely used. Effectively says “I have no opinion about this.” |
-all (reject) and ~all (suspicious) count as “failed” for DMARC purposes. The difference only matters for email providers that don’t use DMARC.
The Building Blocks (Mechanisms)
ip4 and ip6 — Specific Server Addresses
Allow email from a specific server address (IP address) or range of addresses. This is the most direct way to authorise a sender. Doesn’t count toward the 10-lookup limit (see below).
ip4:203.0.113.0/24 # A range of 256 addresses
ip6:2001:db8::1 # One specific IPv6 server
ip6:2001:db8::/32 # A range of IPv6 addresses
a — Check Your Domain’s Address
Allows email from whatever server your domain’s web address points to. Useful if the same server handles your website and email. Costs 1 lookup.
a:mail.example.com # Allow from a specific subdomain’s server
a:example.com/24 # Allow the whole range near that server
mx — Check Your Mail Servers
Allows email from the servers listed as your domain’s mail servers (MX records). Costs 1 lookup (plus 1 for each mail server that needs resolving).
mx:example.com # Allow from another domain’s mail servers
mx/24 # Allow the whole range near mail server IPs
include — Use Another Service’s List
This is how you authorise third-party services (like Google, Mailchimp, or SendGrid). It tells email providers: “also check the sender list for this other domain — if the sender is on their list, it’s allowed to send for us too.” Costs 1 lookup (plus any lookups needed by the included list).
include:spf.protection.outlook.com # Microsoft 365
include:sendgrid.net # SendGrid
include:servers.mcsv.net # Mailchimp
include can trigger further lookups behind the scenes. For example, Google’s list (include:_spf.google.com) alone uses 3–4 lookups. This is the most common reason people exceed the 10-lookup limit.
exists — Advanced: Check If a Record Exists
An advanced option that checks whether a specific DNS entry exists. Used for sophisticated setups with dynamic checking. Costs 1 lookup. Most businesses won’t need this.
all — The Default Rule
Matches everything — this is your “catch-all” at the end of the record. It says what to do with any sender that wasn’t matched by the entries above.
~all # Flag as suspicious but deliver (good during setup)
?all # No opinion on unlisted senders (rarely useful)
+all # Allow everyone — defeats the entire purpose (never use this!)
Extra Options (Modifiers)
redirect
Points to another domain’s SPF record to use instead of yours. Unlike include, this completely replaces your record with theirs — there’s no “fall through” to the rest of your record. Costs 1 lookup.
Only takes effect if nothing else in the record matches. Can’t be used together with all.
exp — Custom Error Message
Lets you provide a human-readable message explaining why an email was rejected. Rarely used in practice.
The 10-Lookup Limit
The standard that defines SPF limits you to 10 DNS lookups when processing an SPF record. If your record goes over this limit, the entire check breaks — it returns a permanent error, which effectively means SPF doesn’t work at all for your domain.
What Counts Toward the Limit — and What Doesn’t
include:(plus any lookups inside it!)a/a:mx/mx:exists:redirect=
ip4:(direct address — no lookup needed)ip6:(direct address — no lookup needed)all- The initial lookup of your SPF record itself
Example: Counting Your Lookups
include:_spf.google.com # 1 + ~3 hidden lookups = 4 total
include:spf.protection.outlook.com # 1 + ~2 hidden lookups = 3 total
include:sendgrid.net # 1 + ~1 hidden lookup = 2 total
ip4:203.0.113.5 # 0 lookups (it’s a direct address)
a # 1 lookup
-all
# Total: ~10 lookups — right at the limit!
SPF Flattening — A Workaround for the Limit
If you’re hitting the 10-lookup limit, SPF flattening replaces your include: entries with the actual server addresses they point to. This eliminates the lookups, but it comes with trade-offs:
- Service providers change their server addresses without telling you — you need to re-flatten regularly (at least weekly)
- The resulting record is much longer and harder to read
- Debugging problems becomes more difficult
- Consider using an SPF flattening service that handles this automatically
Common Service Provider Entries
Here are the include: entries for common email services, along with how many lookups each one costs behind the scenes:
| Service | What to Add | 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: Just Google Workspace
Typical: Google + a Marketing Platform
Complex: Multiple Services + Your Own Server
A Domain That Doesn’t Send Email
This says “nobody is allowed to send email from this domain.” Important for protecting domains you own but don’t use for email (like parked domains or redirect-only domains).
Best Practices
- Use
ip4:/ip6:where possible — they don’t cost any lookups - End with
-all(hard reject) once you’ve confirmed all your legitimate senders are listed - Use
~all(soft fail) while you’re still figuring out your senders - Review your SPF record regularly — remove entries for services you no longer use
- Only have one SPF record per domain — having two causes both to break
- Create SPF records on all your domains and subdomains — even ones that don’t send email (use
v=spf1 -all) - Use our Domain Checker to verify your SPF record and count your lookups
For the bigger picture on how SPF works with DKIM and DMARC, see SPF vs DKIM vs DMARC — What’s the Difference?. For a step-by-step introduction, see What is SPF?.