What's Actually Happening
Email SPF (Sender Policy Framework) records fail validation. Emails are marked as spam or rejected by receiving servers.
The Error You'll See
DNS query shows no SPF:
```bash $ dig txt example.com
# No SPF record found ```
SPF validation tool error:
SPF record not found
No SPF record found for domain example.comEmail header shows failure:
Authentication-Results: mx.google.com;
spf=fail (google.com: domain of sender@example.com does not designate IP) smtp.mailfrom=example.comBounce message:
550-5.7.26 The MAIL FROM domain has an SPF record with a hard fail policy
550-5.7.26 that does not match the sender IP addressWhy This Happens
- 1.SPF record missing - No SPF TXT record in DNS
- 2.Wrong DNS location - SPF record on wrong domain
- 3.Syntax error - Invalid SPF record format
- 4.Too many DNS lookups - Exceeding 10 lookup limit
- 5.Wrong IP included - Authorized IPs don't match sending servers
- 6.Multiple SPF records - More than one SPF TXT record
Step 1: Check Current SPF Record
```bash # Check SPF record: dig txt example.com +short
# Look for SPF record (starts with v=spf1)
# Check with nslookup: nslookup -type=txt example.com
# Check with specialized tool: dig txt example.com | grep spf
# Check SPF record specifically: dig txt example.com +short | grep "v=spf1"
# Check all TXT records: dig txt example.com +short
# Check with Google DNS: dig txt example.com @8.8.8.8
# Check with Cloudflare DNS: dig txt example.com @1.1.1.1 ```
Step 2: Validate SPF Syntax
```bash # Basic SPF syntax: v=spf1 [mechanisms] [qualifier]
# Common mechanisms: # ip4: authorize IPv4 address # ip6: authorize IPv6 address # a: authorize domain's A record # mx: authorize domain's MX servers # include: authorize another domain's SPF # all: default for all other IPs
# Qualifiers: # + (pass) - default # - (fail) - reject unauthorized # ~ (softfail) - accept but mark # ? (neutral) - no policy
# Validate SPF syntax: # Use online tools: # https://www.kitterman.com/spf/validate.html # https://mxtoolbox.com/spf.aspx
# Check syntax manually: # Valid: v=spf1 ip4:192.0.2.1 -all # Invalid: v=spf1 ip4:192.0.2.1 -all extra ```
Step 3: Create Correct SPF Record
```bash # Basic SPF record (authorize only one IP): v=spf1 ip4:192.0.2.1 -all
# Authorize multiple IPs: v=spf1 ip4:192.0.2.1 ip4:192.0.2.2 -all
# Authorize IP range: v=spf1 ip4:192.0.2.0/24 -all
# Authorize domain's A record: v=spf1 a -all
# Authorize domain's MX servers: v=spf1 mx -all
# Authorize Google Workspace: v=spf1 include:_spf.google.com ~all
# Authorize Microsoft 365: v=spf1 include:spf.protection.outlook.com -all
# Authorize multiple providers: v=spf1 include:_spf.google.com include:spf.protection.outlook.com -all
# Authorize all (not recommended): v=spf1 +all
# Recommended format: v=spf1 a mx include:_spf.google.com ~all ```
Step 4: Fix Multiple SPF Records
```bash # Check for multiple SPF records: dig txt example.com +short | grep "v=spf1"
# WRONG: Multiple SPF records break validation # Only ONE SPF TXT record allowed!
# If multiple found, combine them: # Before: # "v=spf1 include:_spf.google.com ~all" # "v=spf1 include:spf.protection.outlook.com ~all"
# After (combined): # "v=spf1 include:_spf.google.com include:spf.protection.outlook.com ~all"
# Remove duplicate SPF records from DNS # Keep only the combined record ```
Step 5: Fix DNS Lookup Limit
```bash # SPF limits to 10 DNS lookups # Each "include" and "a" mechanism counts as lookup
# Check DNS lookup count: # https://mxtoolbox.com/spf.aspx
# Example that may exceed limit: v=spf1 include:spf1.provider1.com include:spf2.provider2.com include:spf3.provider3.com include:spf4.provider4.com a mx -all
# Fix by using SPF flattening: # Instead of include:domain.com # Use the IPs from that domain: # ip4:1.2.3.4 ip4:5.6.7.8
# Or use redirect: v=spf1 redirect=_spf.example.com
# Create _spf.example.com with flattened IPs: _spf.example.com. IN TXT "v=spf1 ip4:1.2.3.0/24 -all" ```
Step 6: Configure SPF in DNS
```bash # For common DNS providers:
# Cloudflare: # DNS -> Add Record # Type: TXT # Name: @ (or leave blank) # Content: v=spf1 include:_spf.google.com ~all
# GoDaddy: # DNS Management -> Add # Type: TXT # Host: @ # TXT Value: v=spf1 include:_spf.google.com ~all
# Namecheap: # Domain -> Advanced DNS -> Add New Record # Type: TXT Record # Host: @ # Value: v=spf1 include:_spf.google.com ~all
# AWS Route 53: # Hosted Zone -> Create Record # Record type: TXT # Value: "v=spf1 include:_spf.google.com ~all"
# Google Domains: # DNS -> Custom resource records # Type: TXT # Name: @ # Data: v=spf1 include:_spf.google.com ~all ```
Step 7: Test SPF Validation
```bash # Test SPF record: # Option 1: Online tools # https://www.kitterman.com/spf/validate.html # https://mxtoolbox.com/spf.aspx
# Option 2: Command line dig txt example.com +short | grep "v=spf1"
# Option 3: Check mail header # Look at Authentication-Results in received email
# Option 4: Send test email: # Use services like: # mail-tester.com # glockapps.com
# Test specific IP against SPF: # https://spf-visualiser.com
# Check SPF from specific IP: python3 -c " import dns.resolver answers = dns.resolver.resolve('example.com', 'TXT') for rdata in answers: if 'v=spf1' in str(rdata): print(rdata) " ```
Step 8: Handle Subdomains
```bash # SPF for subdomain: # Option 1: Separate record for subdomain sub.example.com. IN TXT "v=spf1 ip4:192.0.2.1 -all"
# Option 2: Inherit from parent (redirect) sub.example.com. IN TXT "v=spf1 redirect=example.com"
# Option 3: Wildcard SPF (not recommended) *.example.com. IN TXT "v=spf1 redirect=example.com"
# Check subdomain SPF: dig txt sub.example.com +short
# Check if subdomain inherits: # If no SPF on subdomain, check parent ```
Step 9: Check Email Sending IPs
```bash # Find your sending IP addresses:
# Check email headers: # Received: from mail.example.com ([192.0.2.1])
# Check mail server: dig mx example.com +short # Get IPs of mail servers dig mail.example.com +short
# For Google Workspace: # IPs: https://support.google.com/a/answer/60764
# For Microsoft 365: # IPs: https://docs.microsoft.com/microsoft-365/enterprise/urls-and-ip-address-ranges
# For SendGrid: # Use: include:sendgrid.net
# For Mailchimp: # Use: include:servers.mcsv.net
# For Amazon SES: # Use: include:amazonses.com ```
Step 10: SPF Verification Script
```bash # Create verification script: cat << 'EOF' > /usr/local/bin/check-spf.sh #!/bin/bash
DOMAIN=$1
if [ -z "$DOMAIN" ]; then echo "Usage: $0 domain.com" exit 1 fi
echo "=== SPF Record for $DOMAIN ===" SPF=$(dig txt $DOMAIN +short | grep "v=spf1") if [ -z "$SPF" ]; then echo "No SPF record found!" else echo "$SPF" fi
echo "" echo "=== SPF Record Count ===" COUNT=$(dig txt $DOMAIN +short | grep -c "v=spf1") echo "Found $COUNT SPF record(s)" if [ $COUNT -gt 1 ]; then echo "WARNING: Multiple SPF records will cause validation failure!" fi
echo "" echo "=== All TXT Records ===" dig txt $DOMAIN +short
echo "" echo "=== MX Servers ===" dig mx $DOMAIN +short
echo "" echo "=== SPF Test ===" if [ -n "$SPF" ]; then echo "Testing SPF syntax..." # Basic syntax check if echo "$SPF" | grep -q "^\"v=spf1"; then echo "SPF syntax looks valid" else echo "Warning: SPF may have syntax issues" fi fi
echo "" echo "=== Online Validation ===" echo "Validate at: https://mxtoolbox.com/spf.aspx?domain=$DOMAIN" echo "Validate at: https://www.kitterman.com/spf/validate.html" EOF
chmod +x /usr/local/bin/check-spf.sh
# Usage: /usr/local/bin/check-spf.sh example.com
# Check all domains: for domain in example.com example.org; do echo "--- $domain ---" /usr/local/bin/check-spf.sh $domain done ```
SPF Record Checklist
| Check | Command | Expected | |
|---|---|---|---|
| SPF record exists | dig txt \ | grep spf | Record found |
| Single record | grep -c spf | 1 | |
| Syntax valid | online validator | Pass | |
| Sending IPs included | compare | IPs match | |
| DNS lookups | online validator | Under 10 | |
| Propagation | dig from multiple DNS | Same result |
Verify the Fix
```bash # After fixing SPF record
# 1. Check record dig txt example.com +short | grep spf // v=spf1 include:_spf.google.com ~all
# 2. Validate syntax # Use mxtoolbox.com/spf.aspx // Pass
# 3. Check lookup count // Under 10
# 4. Send test email # Check Authentication-Results // spf=pass
# 5. Test from different IPs # Use mail-tester.com // SPF passes
# 6. Monitor email deliverability # Check spam folder reduction // Emails delivered to inbox ```
Related Issues
- [Fix Email Not Sending](/articles/fix-email-not-sending)
- [Fix Email DKIM Record Not Validating](/articles/fix-email-dkim-record-not-validating)
- [Fix Email DMARC Record Not Validating](/articles/fix-email-dmarc-record-not-validating)