SPF (Sender Policy Framework) records authorize specific servers to send email on behalf of your domain. This comprehensive guide covers SPF record generation, optimization, and best practices for maximum effectiveness.
SPF Record Basics
SPF Record Structure
# Basic SPF Record Format
"v=spf1 [mechanisms] [modifiers] [qualifier]"
## Core Components
# Version (required)
v=spf1 # SPF version 1
# Mechanisms (authorize mail sources)
include:domain.com # Include another domain's SPF policy
a # Authorize A record IPs
mx # Authorize MX record IPs
ip4:203.0.113.1 # Authorize specific IPv4 address
ip6:2001:db8::1 # Authorize specific IPv6 address
exists:verify.domain # Check if domain exists
# Qualifiers (specify action)
+ # Pass (default, usually omitted)
- # Fail (hard fail)
~ # SoftFail (soft fail)
? # Neutral (no policy)
# Modifiers (additional instructions)
redirect=domain.com # Redirect to another domain's policy
exp=explanation.domain # Explanation text for failures
## Complete Example
"v=spf1 mx a include:_spf.google.com ip4:203.0.113.1 ~all"Common SPF Mechanisms Explained
# SPF Mechanisms with Examples
## Include Mechanism
# Most common - includes another domain's SPF policy
include:_spf.google.com # Google Workspace
include:spf.protection.outlook.com # Microsoft 365
include:sendgrid.net # SendGrid
include:mailgun.org # Mailgun
include:_spf.salesforce.com # Salesforce
## A Mechanism
# Authorizes IPs in domain's A record
a # Authorize example.com A record IPs
a:mail.example.com # Authorize mail.example.com A record IPs
## MX Mechanism
# Authorizes IPs in domain's MX records
mx # Authorize example.com MX record IPs
mx:mail.example.com # Authorize mail.example.com MX record IPs
## IP4/IP6 Mechanisms
# Direct IP authorization
ip4:203.0.113.1 # Single IPv4 address
ip4:203.0.113.0/24 # IPv4 subnet
ip6:2001:db8::1 # Single IPv6 address
ip6:2001:db8::/64 # IPv6 subnet
## Exists Mechanism
# Conditional authorization based on DNS lookup
exists:%{i}.whitelist.example.com # Check if IP is in whitelist
exists:%{s}.authorized.domain.com # Check if sender is authorized
## All Mechanism
# Catch-all at the end of record
-all # Hard fail all others
~all # Soft fail all others
+all # Pass all others (not recommended)
?all # Neutral for all othersSPF Generator Guidelines
Step-by-Step SPF Creation Process
# SPF Generation Process
## Step 1: Inventory Your Mail Sources
# List all systems that send email for your domain:
# Internal mail servers
- Primary mail server: mail.example.com (203.0.113.1)
- Backup mail server: backup.example.com (203.0.113.2)
# Third-party services
- Google Workspace: _spf.google.com
- Marketing platform: SendGrid
- CRM system: Salesforce
- Support system: Zendesk
# Web applications
- Website contact forms: 203.0.113.10
- E-commerce notifications: 203.0.113.11
## Step 2: Choose Appropriate Mechanisms
# For each mail source, select the best mechanism:
# Google Workspace → include:_spf.google.com
# (Recommended: uses Google's maintained SPF)
# SendGrid → include:sendgrid.net
# (Or ip4: if you have dedicated IPs)
# Internal servers → ip4: or mx
# ip4: for specific IPs, mx for MX record IPs
# Web servers → ip4: with specific addresses
## Step 3: Construct SPF Record
# Start with version
"v=spf1"
# Add mechanisms in order of frequency (most common first)
"v=spf1 include:_spf.google.com"
# Add additional services
"v=spf1 include:_spf.google.com include:sendgrid.net"
# Add internal servers
"v=spf1 include:_spf.google.com include:sendgrid.net mx"
# Add specific IPs
"v=spf1 include:_spf.google.com include:sendgrid.net mx ip4:203.0.113.10"
# End with all mechanism
"v=spf1 include:_spf.google.com include:sendgrid.net mx ip4:203.0.113.10 ~all"
## Step 4: Validate and Optimize
# Check DNS lookup count (must be ≤ 10)
# Verify syntax correctness
# Test with known good/bad IPs
# Implement gradually (~all before -all)Provider-Specific SPF Examples
# SPF Records by Email Provider
## Google Workspace Only
example.com. IN TXT "v=spf1 include:_spf.google.com ~all"
# Benefits:
# - Google maintains the IP ranges
# - Automatic updates when Google changes IPs
# - Only 1 DNS lookup
## Microsoft 365 Only
example.com. IN TXT "v=spf1 include:spf.protection.outlook.com ~all"
## Hybrid: Google + Microsoft
example.com. IN TXT "v=spf1 include:_spf.google.com include:spf.protection.outlook.com ~all"
## Marketing Platforms
# SendGrid
example.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# Mailgun
example.com. IN TXT "v=spf1 include:_spf.google.com include:mailgun.org ~all"
# Mailchimp
example.com. IN TXT "v=spf1 include:_spf.google.com include:servers.mcsv.net ~all"
# Campaign Monitor
example.com. IN TXT "v=spf1 include:_spf.google.com include:_spf.createsend.com ~all"
## CRM and Business Systems
# Salesforce
example.com. IN TXT "v=spf1 include:_spf.google.com include:_spf.salesforce.com ~all"
# HubSpot
example.com. IN TXT "v=spf1 include:_spf.google.com include:_spf.hubspotemail.net ~all"
# Zendesk
example.com. IN TXT "v=spf1 include:_spf.google.com include:mail.zendesk.com ~all"
## Complex Multi-Service Example
example.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net include:_spf.salesforce.com include:mail.zendesk.com mx ip4:203.0.113.0/24 ~all"
# DNS Lookup Count: 7 (under 10 limit)
# - include:_spf.google.com (1)
# - include:sendgrid.net (1)
# - include:_spf.salesforce.com (1)
# - include:mail.zendesk.com (1)
# - mx (1)
# - ip4: (0 - no lookup required)
# - Total: 5 lookupsSPF Optimization Strategies
DNS Lookup Optimization
SPF has a limit of 10 DNS lookups. Exceeding this causes SPF failure:
# SPF Optimization Techniques
## Problem: Too Many DNS Lookups
# This SPF record exceeds the 10 lookup limit:
× "v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org include:_spf.salesforce.com include:servers.mcsv.net include:_spf.createsend.com include:mail.zendesk.com a mx ~all"
# DNS Lookups: 9 (at the limit)
## Solution 1: Replace includes with IP ranges
• "v=spf1 include:_spf.google.com ip4:167.89.0.0/17 ip4:198.61.254.0/24 ip4:208.75.123.0/24 mx ~all"
# Benefits:
# - Reduced from 9 to 3 DNS lookups
# - Faster SPF evaluation
# - More reliable (no dependency on third-party DNS)
# Drawbacks:
# - Manual maintenance required
# - Risk of missing IP changes
## Solution 2: Create SPF include hierarchy
# Create subdomain with consolidated includes
_spf-vendors.example.com. IN TXT "v=spf1 include:sendgrid.net include:mailgun.org include:_spf.salesforce.com ~all"
# Main SPF references the subdomain
example.com. IN TXT "v=spf1 include:_spf.google.com include:_spf-vendors.example.com mx ~all"
# Total lookups: 3 in main record
# Additional lookups occur in subdomain evaluation
## Solution 3: Hybrid approach
# Keep dynamic services as includes, use IPs for static
example.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net ip4:203.0.113.0/24 ~all"
# Guidelines:
# - Use include: for services that manage their own IPs
# - Use ip4: for static internal servers
# - Group related services in subdomain SPF records
# - Monitor and update IP ranges quarterlyMechanism Ordering Optimization
# SPF Mechanism Ordering Best Practices
## Principle: Most Frequent Sources First
# SPF evaluation stops at first match
# Order mechanisms by email volume for efficiency
## Example: E-commerce Company
# Volume distribution:
# - Google Workspace: 60% of emails
# - Marketing (SendGrid): 30% of emails
# - Internal servers: 8% of emails
# - CRM (Salesforce): 2% of emails
# Optimized order:
• "v=spf1 include:_spf.google.com include:sendgrid.net mx include:_spf.salesforce.com ~all"
# Poor order:
× "v=spf1 include:_spf.salesforce.com mx include:sendgrid.net include:_spf.google.com ~all"
## Geographic Optimization
# For global organizations, consider regional SPF records
# US traffic
us.example.com. IN TXT "v=spf1 include:_spf.google.com include:us-sendgrid.net ~all"
# EU traffic
eu.example.com. IN TXT "v=spf1 include:_spf.google.com include:eu-sendgrid.net ~all"
# Main domain redirects based on location
example.com. IN TXT "v=spf1 redirect=us.example.com"Advanced SPF Features
SPF Macros
# SPF Macros for Dynamic Authorization
## Common SPF Macros
%{i} = Sending IP address
%{s} = Sender email address
%{d} = Domain from sender
%{l} = Local part of sender
%{o} = Domain from sender
%{h} = HELO/EHLO domain
%{r} = Receiving domain
## Macro Examples
# Customer-specific IP authorization
"v=spf1 exists:%{i}.customers.example.com ~all"
# Checks if sending IP exists in customers.example.com DNS
# Sender-based authorization
"v=spf1 exists:%{s}.authorized.example.com ~all"
# Checks if sender email is in authorized list
# Dynamic subdomain checking
"v=spf1 exists:%{l}.%{d}.trusted.example.com ~all"
# Checks user@domain against trusted list
## Practical Macro Use Cases
# Service provider with customer IPs
# Customer 1: 203.0.113.1 → 203.0.113.1.customers.example.com
# Customer 2: 203.0.113.2 → 203.0.113.2.customers.example.com
"v=spf1 exists:%{ir}.customers.example.com ~all"
# %{ir} reverses IP octets for DNS lookup
# Domain-based partner authorization
"v=spf1 include:spf.%{d} ~all"
# Automatically includes SPF for sender domain
# partner1.com → include:spf.partner1.com
# partner2.com → include:spf.partner2.com
## Macro Modifiers
%{ir} = Reverse IP (1.113.0.203 becomes 203.0.113.1)
%{s1r} = First part of reversed sender
%{d2} = Last 2 labels of domain
%{l1r+} = All but first part of local, reversedSPF Redirect and Explanation
# Advanced SPF Modifiers
## Redirect Modifier
# Replaces current SPF policy with another domain's policy
# Basic redirect
example.com. IN TXT "v=spf1 redirect=spf.example.org"
# This is equivalent to:
example.com. IN TXT "v=spf1 include:spf.example.org"
# Redirect use cases:
# - Centralized SPF management
# - Brand domain consolidation
# - Acquisition/merger transitions
# Multi-brand redirect example
brand1.com. IN TXT "v=spf1 redirect=corporate-spf.example.com"
brand2.com. IN TXT "v=spf1 redirect=corporate-spf.example.com"
brand3.com. IN TXT "v=spf1 redirect=corporate-spf.example.com"
# Central policy
corporate-spf.example.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
## Explanation Modifier
# Provides custom error messages for SPF failures
# Basic explanation
example.com. IN TXT "v=spf1 include:_spf.google.com ~all exp=explain.example.com"
# Explanation domain
explain.example.com. IN TXT "Email from %{i} not authorized to send from %{d}. Please contact [email protected]"
# Dynamic explanations with macros
explain.example.com. IN TXT "Unauthorized email from IP %{i} for domain %{d}. Sender: %{s}. Contact: abuse@%{d}"
# Explanation best practices:
# - Keep messages under 255 characters
# - Include contact information
# - Avoid technical jargon
# - Provide actionable guidance
# - Use macros for personalization
## Combined Advanced Example
example.com. IN TXT "v=spf1 exists:%{i}.whitelist.example.com include:_spf.google.com ~all exp=explain.example.com"
# This record:
# 1. Checks if sending IP is whitelisted
# 2. Includes Google Workspace SPF
# 3. Soft fails others with custom explanation
# 4. Provides helpful error message for failuresSPF Testing and Validation
Manual SPF Testing
# SPF Testing Methods
## Command Line Testing
# Check SPF record exists
dig TXT example.com | grep "v=spf1"
nslookup -type=TXT example.com
# Test SPF from multiple DNS servers
dig @8.8.8.8 TXT example.com
dig @1.1.1.1 TXT example.com
dig @208.67.222.222 TXT example.com
# Count DNS lookups in SPF chain
# Manual method: trace each include
dig TXT _spf.google.com
dig TXT sendgrid.net
## SPF Validation Tools
# Python SPF library testing
python3 -c "
import spf
result, explanation = spf.check2('203.0.113.1', '[email protected]', 'example.com')
print(f'Result: {result}, Explanation: {explanation}')
"
# Expected results:
# pass - SPF authentication passed
# fail - SPF authentication failed (hard fail)
# softfail - SPF authentication failed (soft fail)
# neutral - SPF policy makes no assertion
# none - No SPF record found
# temperror - Temporary DNS failure
# permerror - Permanent SPF syntax error
## Email Header Analysis
# Look for SPF results in received email headers
# Gmail headers
Received-SPF: pass (google.com: domain of [email protected] designates 74.125.130.26 as permitted sender) client-ip=74.125.130.26;
Authentication-Results: mx.google.com; spf=pass (google.com: domain of [email protected] designates 74.125.130.26 as permitted sender) smtp.mailfrom=example.com
# Outlook headers
Authentication-Results: spf=pass (sender IP is 40.107.22.136) smtp.mailfrom=example.com
# Yahoo headers
Received-SPF: pass (domain of [email protected] designates 209.85.220.41 as permitted sender)
## Automated Testing Script
#!/bin/bash
# SPF testing script
DOMAIN="example.com"
TEST_IPS=("203.0.113.1" "203.0.113.2" "192.0.2.1")
TEST_SENDER="test@$DOMAIN"
echo "Testing SPF for $DOMAIN"
# Check SPF record exists
SPF_RECORD=$(dig +short TXT $DOMAIN | grep "v=spf1")
if [ -z "$SPF_RECORD" ]; then
echo "ERROR: No SPF record found for $DOMAIN"
exit 1
fi
echo "SPF Record: $SPF_RECORD"
# Test each IP
for ip in "${TEST_IPS[@]}"; do
echo "Testing IP: $ip"
# Use SPF testing tool (requires pyspf)
python3 -c "
import spf
result, explanation = spf.check2('$ip', '$TEST_SENDER', '$DOMAIN')
print(f'IP $ip: {result}')
"
doneCommon SPF Errors and Fixes
# SPF Troubleshooting Guide
## Error 1: PermError - Too Many DNS Lookups
Error: "permanent error in processing during lookup"
Cause: More than 10 DNS lookups in SPF evaluation
# Problem record (11 lookups)
× "v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org include:_spf.salesforce.com include:servers.mcsv.net include:_spf.createsend.com include:mail.zendesk.com include:_spf.hubspotemail.net a mx exists:check.example.com ~all"
# Solution: Consolidate includes
• "v=spf1 include:_spf.google.com ip4:167.89.0.0/17 ip4:198.61.254.0/24 a mx ~all"
## Error 2: PermError - Syntax Error
Error: "permanent error in processing"
Cause: Invalid SPF syntax
# Common syntax errors:
× "spf1 include:_spf.google.com ~all" # Missing v=
× "v=spf1 include:google.com ~all" # Wrong include domain
× "v=spf1 include:_spf.google.com -all" # Missing ~ or + before all
× "v=spf1 include:_spf.google.com all" # Missing qualifier
# Correct syntax:
• "v=spf1 include:_spf.google.com ~all"
## Error 3: Multiple SPF Records
Error: "permanent error in processing"
Cause: Multiple TXT records with v=spf1
# Problem: Multiple records
× example.com. IN TXT "v=spf1 include:_spf.google.com ~all"
× example.com. IN TXT "v=spf1 mx ~all"
# Solution: Combine into single record
• example.com. IN TXT "v=spf1 include:_spf.google.com mx ~all"
## Error 4: Include Loop
Error: "permanent error in processing"
Cause: Circular include references
# Problem: Circular includes
example.com includes partner.com
partner.com includes example.com
# Solution: Break the loop
example.com. IN TXT "v=spf1 include:_spf.google.com ip4:203.0.113.1 ~all"
partner.com. IN TXT "v=spf1 include:_spf.google.com ip4:198.51.100.1 ~all"
## Error 5: Record Too Long
Error: SPF record exceeds 255 characters
Cause: Single TXT record character limit
# Problem: Long record
× "v=spf1 include:very-long-service-name.com include:another-very-long-service.com include:yet-another-extremely-long-service-name.com include:final-very-long-service-name.com ~all"
# Solution 1: Use shorter includes
• "v=spf1 include:_spf.example.com ~all"
_spf.example.com. IN TXT "v=spf1 include:service1.com include:service2.com ~all"
# Solution 2: Use IP ranges instead of includes
• "v=spf1 ip4:203.0.113.0/24 ip4:198.51.100.0/24 ~all"SPF Migration and Management
Safe SPF Updates
# SPF Update Best Practices
## Current SPF Record
example.com. IN TXT "v=spf1 include:_spf.google.com ~all"
## Adding New Service (SendGrid)
# Step 1: Verify new service requirements
# SendGrid requires: include:sendgrid.net
# OR dedicated IPs: ip4:x.x.x.x
# Step 2: Test updated record syntax
# New record: "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# Step 3: Verify DNS lookup count
# Current: 1 lookup (include:_spf.google.com)
# After: 2 lookups (include:_spf.google.com + include:sendgrid.net)
# Status: OK (under 10 limit)
# Step 4: Deploy with short TTL first
example.com. 300 IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# Step 5: Test from SendGrid
# Send test emails and verify SPF pass
# Step 6: Increase TTL after confirmation
example.com. 3600 IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
## Removing Service Safely
# Current: Google + SendGrid + Mailgun
example.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org ~all"
# Removing Mailgun:
# Step 1: Verify no emails from Mailgun for 30 days
# Check email logs and DMARC reports
# Step 2: Deploy updated record
example.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# Step 3: Monitor for SPF failures
# Watch DMARC reports for unexpected failures
# Step 4: Document change
# Update SPF documentation and runbooks
## Emergency SPF Rollback
# If SPF update causes delivery issues:
# Step 1: Immediate rollback to previous record
# Use DNS provider's rollback feature if available
# Step 2: Clear DNS cache
# May need to wait for TTL expiration
# Step 3: Verify rollback success
dig TXT example.com
# Step 4: Investigate and fix issue
# Test new configuration thoroughly before re-deploymentUse our SPF Generator tool to create optimized SPF records with automatic DNS lookup counting and syntax validation.
SPF Best Practices Summary
- **Start simple**: Begin with basic record, add complexity gradually
- **Monitor DNS lookups**: Stay under 10 lookup limit for reliability
- **Use include for dynamic services**: Let providers manage their IP ranges
- **Use IP4/IP6 for static servers**: Direct IP authorization for internal servers
- **Order by frequency**: Most common mail sources first
- **Test before deployment**: Validate syntax and functionality
- **Implement gradually**: Use ~all before -all during testing
- **Document changes**: Maintain records of SPF modifications
- **Monitor regularly**: Check DMARC reports for SPF issues
- **Plan for growth**: Design SPF to accommodate future services