SPF (Sender Policy Framework)¶
Understanding SPF and how it protects your domain from email spoofing.
What is SPF?¶
SPF (Sender Policy Framework) is an email authentication protocol that allows domain owners to specify which mail servers are authorized to send email on behalf of their domain. It's published as a DNS TXT record and helps prevent spammers from sending emails with forged "From" addresses from your domain.
The Problem SPF Solves¶
Without SPF, anyone can send an email claiming to be from your domain:
From: ceo@yourcompany.com (Attacker's server)
To: employee@yourcompany.com
Subject: Urgent: Wire Transfer Needed
Please transfer $50,000 to this account immediately...
SPF allows you to publish a list of authorized mail servers, so receiving servers can verify that emails actually came from your infrastructure.
How SPF Works¶
sequenceDiagram
participant Sender as Mail Server
participant DNS as DNS Server
participant Receiver as Receiving Server
Sender->>Receiver: Email from user@example.com
Note over Sender,Receiver: SMTP connection from IP 192.0.2.1
Receiver->>DNS: Query SPF record for example.com
DNS->>Receiver: v=spf1 ip4:192.0.2.0/24 -all
Receiver->>Receiver: Check if 192.0.2.1 is authorized
Note over Receiver: IP matches! SPF Pass
Receiver->>Receiver: Accept email
The SPF Check Process¶
- Email arrives at receiving mail server
- Receiver extracts the domain from the
MAIL FROM(envelope sender) - DNS lookup is performed for the domain's SPF record
- IP address check - Receiver compares sending IP against authorized IPs/ranges
- Result returned - Pass, Fail, SoftFail, Neutral, or PermError
SPF Record Anatomy¶
Basic Structure¶
Example SPF Record¶
Let's break this down:
| Component | Description |
|---|---|
v=spf1 |
Version identifier - Must always be first |
ip4:192.0.2.0/24 |
IP mechanism - Authorizes this IP range |
include:_spf.google.com |
Include mechanism - Includes Google's SPF record |
-all |
All mechanism - Reject all other senders (hard fail) |
SPF Mechanisms¶
Mechanisms specify which IP addresses are authorized to send mail.
1. IP Mechanisms¶
IPv4 Addresses
# Single IP
v=spf1 ip4:192.0.2.1 -all
# IP range (CIDR notation)
v=spf1 ip4:192.0.2.0/24 -all
# Multiple IPs
v=spf1 ip4:192.0.2.1 ip4:198.51.100.1 -all
IPv6 Addresses
2. Include Mechanism¶
Include another domain's SPF record:
Common Provider Includes:
Google Workspace:
Microsoft 365:
Mailgun:
SendGrid:
Amazon SES:
3. A and MX Mechanisms¶
A Mechanism - Authorize the domain's A record IP:
MX Mechanism - Authorize IPs from MX records:
4. Exists Mechanism¶
Advanced mechanism for complex lookups:
SPF Qualifiers¶
Qualifiers determine what action to take when a mechanism matches:
| Qualifier | Symbol | Action | Example |
|---|---|---|---|
| Pass | + (default) |
Accept the email | +ip4:192.0.2.1 or ip4:192.0.2.1 |
| Fail | - |
Reject the email | -all |
| SoftFail | ~ |
Accept but mark | ~all |
| Neutral | ? |
No policy statement | ?all |
The "All" Mechanism¶
The all mechanism is the catch-all that matches everything not matched by previous mechanisms:
# Hard fail - Reject unauthorized senders (RECOMMENDED)
v=spf1 include:_spf.google.com -all
# Soft fail - Accept but mark as suspicious
v=spf1 include:_spf.google.com ~all
# Neutral - No opinion (provides minimal protection)
v=spf1 include:_spf.google.com ?all
# Pass all - DANGEROUS! Never use this
v=spf1 +all
Best Practice: Always use -all for maximum protection.
Common SPF Configurations¶
Scenario 1: Google Workspace Only¶
Scenario 2: Microsoft 365 Only¶
Scenario 3: Multiple Email Providers¶
Scenario 4: Email Provider + Custom Servers¶
Scenario 5: No Email Sent from Domain¶
This explicitly states that NO servers are authorized to send email from this domain.
SPF Macros (Advanced)¶
SPF supports macros for dynamic record construction:
| Macro | Expands to |
|---|---|
%{s} |
Sender (MAIL FROM) |
%{l} |
Local part of sender |
%{o} |
Domain of sender |
%{d} |
Current domain |
%{i} |
Sending IP address |
%{h} |
HELO/EHLO domain |
Example:
This checks if a DNS record exists for <sending-ip>._spf.example.com.
The 10 DNS Lookup Limit¶
Critical Limitation: SPF records are limited to 10 DNS lookups to prevent abuse.
What Counts as a Lookup?¶
include:- 1 lookupa- 1 lookupmx- 1 lookupexists:- 1 lookupredirect=- 1 lookup
Does NOT count:
- ip4: and ip6: - No lookup required
- all - No lookup
Example: Counting Lookups¶
Lookup count:
1. include:_spf.google.com - 1 lookup (plus any in Google's record)
2. include:mailgun.org - 1 lookup (plus any in Mailgun's record)
3. include:sendgrid.net - 1 lookup (plus any in SendGrid's record)
4. a - 1 lookup
5. mx - 1 lookup
If Google's SPF includes 3 more lookups, Mailgun's includes 2, and SendGrid's includes 2, total = 5 + 3 + 2 + 2 = 12 lookups - This exceeds the limit!
Fixing the 10 Lookup Limit¶
Option 1: Use IP Addresses Instead of Includes¶
# Before (uses lookups)
v=spf1 include:provider1.com include:provider2.com -all
# After (no lookups)
v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.0/24 -all
Option 2: SPF Flattening¶
Use a service that "flattens" includes into IP ranges:
Option 3: Use Subdomains¶
Split sending across subdomains:
# Main domain
example.com. IN TXT "v=spf1 include:_spf.google.com -all"
# Marketing emails
marketing.example.com. IN TXT "v=spf1 include:sendgrid.net -all"
# Transactional emails
app.example.com. IN TXT "v=spf1 include:mailgun.org -all"
Common SPF Issues¶
Issue 1: Multiple SPF Records¶
Problem:
example.com. IN TXT "v=spf1 include:_spf.google.com -all"
example.com. IN TXT "v=spf1 include:mailgun.org -all"
Why it fails: RFC 7208 requires exactly ONE SPF record. Multiple records cause a PermError.
Fix: Combine into one record:
Issue 2: SPF +all (Pass All)¶
Problem:
Why it's bad: This authorizes ANYONE to send email from your domain.
Fix:
Issue 3: SPF ~all (SoftFail)¶
Problem:
Why it's weak: SoftFail (~all) suggests but doesn't enforce rejection. Many receivers ignore SoftFail.
Fix:
Issue 4: Exceeding 10 DNS Lookups¶
Problem: SPF record triggers more than 10 DNS lookups
Symptoms:
- Emails fail SPF with "PermError"
- ReputeAPI reports SPF_TOO_MANY_LOOKUPS
Fix: See The 10 DNS Lookup Limit section above
Issue 5: Syntax Errors¶
Common mistakes:
# Missing v=spf1
"include:_spf.google.com -all"
# Typo in mechanism
"v=spf1 inclde:_spf.google.com -all"
# Wrong version
"v=spf2 include:_spf.google.com -all"
# Extra spaces
"v=spf1 include:_spf.google.com -all"
Always validate with tools:
- MXToolbox SPF Check
- Google Admin Toolbox
- ReputeAPI /api/v1/check endpoint
SPF Results¶
When a receiving server checks SPF, it returns one of these results:
| Result | Description | Action |
|---|---|---|
| Pass | Sender IP is authorized | Accept email |
| Fail | Sender IP is NOT authorized (hit -all) | Reject or mark as spam |
| SoftFail | Sender IP probably not authorized (~all) | Accept but flag |
| Neutral | No policy statement (?all) | Accept |
| None | No SPF record found | Accept (no SPF protection) |
| TempError | Temporary DNS error | Retry later |
| PermError | Permanent error (syntax, too many lookups) | Treat as Fail |
SPF Alignment (for DMARC)¶
For DMARC to pass, SPF must be aligned with the From header domain.
Relaxed Alignment (Default)¶
The organizational domain must match:
Strict Alignment¶
The exact domain must match:
MAIL FROM: bounce@mail.example.com
From: user@example.com
✗ NOT aligned (mail.example.com ≠ example.com)
DMARC Alignment Setting:
Testing Your SPF Record¶
Using ReputeAPI¶
curl -X GET "https://api.reputeapi.com/api/v1/check?domain=example.com" \
-H "X-API-Key: your-api-key"
Response includes SPF validation:
{
"spf": {
"present": true,
"valid": true,
"record": "v=spf1 include:_spf.google.com -all",
"mechanisms": ["include:_spf.google.com", "-all"],
"warnings": []
},
"issues": []
}
Using Command Line¶
Check SPF record:
Check from specific IP:
# Using Python's pyspf
python3 -m pyspf.check -i 192.0.2.1 -s user@example.com -h mail.example.com
# Using spfquery
spfquery -ip 192.0.2.1 -sender user@example.com -helo mail.example.com
Online Tools¶
SPF Best Practices¶
1. Start with Hard Fail (-all)¶
Use -all not ~all. SoftFail provides minimal protection.
2. Keep It Simple¶
Only include servers that actually send mail:
# Good - Only necessary includes
v=spf1 include:_spf.google.com -all
# Bad - Too many includes
v=spf1 include:provider1 include:provider2 include:provider3 include:provider4 -all
3. Monitor DNS Lookup Count¶
Stay under 10 lookups. Use ReputeAPI to check:
curl "https://api.reputeapi.com/api/v1/check?domain=example.com" \
-H "X-API-Key: your-api-key" | jq '.spf'
4. Use IP Ranges When Possible¶
5. Document Your SPF Record¶
Keep a comment with your DNS records:
; SPF for example.com
; Authorizes Google Workspace and internal mail server
example.com. IN TXT "v=spf1 ip4:192.0.2.100 include:_spf.google.com -all"
6. Test Before Deploying¶
- Create with ~all first (SoftFail for testing)
- Monitor for 1-2 weeks to catch legitimate servers
- Switch to -all once confident
7. Regular Audits¶
Review SPF records quarterly: - Remove unused includes - Update IP ranges - Check for lookup count - Verify all authorized senders
SPF for Subdomains¶
Explicit Subdomain Records¶
# Main domain
example.com. IN TXT "v=spf1 include:_spf.google.com -all"
# Subdomain with different SPF
mail.example.com. IN TXT "v=spf1 ip4:192.0.2.100 -all"
Subdomain Inheritance¶
Without explicit SPF record, subdomains do NOT inherit the parent's SPF. Each subdomain needs its own record.
Protecting unused subdomains:
SPF and DMARC¶
SPF alone doesn't provide full protection. It must work with DMARC:
# SPF Record
example.com. IN TXT "v=spf1 include:_spf.google.com -all"
# DMARC Record (enforces SPF)
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; aspf=r; rua=mailto:dmarc@example.com"
Why both are needed: - SPF validates the envelope sender (MAIL FROM) - DMARC validates the header From address and enforces policy - Together they prevent email spoofing
Learn more: Email Authentication Overview
Migrating Email Providers¶
When changing email providers, update SPF carefully:
Step 1: Add New Provider¶
Step 2: Test New Provider¶
Send test emails and verify SPF passes:
curl "https://api.reputeapi.com/api/v1/check?domain=example.com&refresh=true" \
-H "X-API-Key: your-api-key"
Step 3: Remove Old Provider¶
Wait at least 48 hours between changes to allow DNS propagation.
SPF Validation with ReputeAPI¶
Check Domain's SPF¶
import requests
response = requests.get(
"https://api.reputeapi.com/api/v1/check",
params={"domain": "example.com"},
headers={"X-API-Key": "your-api-key"}
)
result = response.json()
if not result['spf']['present']:
print("❌ No SPF record found")
elif result['spf']['valid']:
print(f"✅ SPF valid: {result['spf']['record']}")
else:
print(f"⚠️ SPF has issues")
# Check for SPF-related issues
spf_issues = [i for i in result['issues'] if i['category'] == 'spf']
for issue in spf_issues:
print(f" [{issue['severity']}] {issue['message']}")
print(f" Fix: {issue['remediation']}")
Get SPF Recommendations¶
response = requests.post(
"https://api.reputeapi.com/api/v1/recommendations",
json={"domain": "example.com"},
headers={"X-API-Key": "your-api-key"}
)
recommendations = response.json()
for rec in recommendations['recommendations']:
if rec['category'] == 'spf':
print(f"Priority: {rec['priority']}")
print(f"Action: {rec['action']}")
print(f"DNS Snippet: {rec['dns_snippet']['value']}")
Common Questions¶
Do I need SPF if I have DMARC?¶
Yes! DMARC relies on SPF (and/or DKIM) passing. Without SPF, DMARC cannot validate your emails.
Can I have multiple SPF records?¶
No. RFC 7208 requires exactly ONE SPF record per domain. Multiple records cause a PermError.
What if I don't send email from my domain?¶
Use a null SPF record:
This explicitly states no servers are authorized.
Should I use ~all or -all?¶
Always use -all for maximum protection. SoftFail (~all) is too lenient and often ignored.
How long does DNS propagation take?¶
Typically 15 minutes to 48 hours. Most DNS servers respect TTL values, so:
Can SPF protect against all phishing?¶
No. SPF only validates the envelope sender. It doesn't protect against: - Display name spoofing - Look-alike domains - Compromised accounts
Use SPF + DKIM + DMARC together for comprehensive protection.
Related Concepts¶
- DKIM Explained - Email cryptographic signatures
- DMARC Explained - Policy enforcement
- Email Authentication - How they work together
- DNS Configuration - Setting up your records
API Resources¶
- GET /api/v1/check - Comprehensive SPF validation
- POST /api/v1/recommendations - Get SPF fix recommendations
- Mailflow Security Score - How SPF affects your score