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:

bash
SPF record not found
No SPF record found for domain example.com

Email header shows failure:

bash
Authentication-Results: mx.google.com;
   spf=fail (google.com: domain of sender@example.com does not designate IP) smtp.mailfrom=example.com

Bounce message:

bash
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 address

Why This Happens

  1. 1.SPF record missing - No SPF TXT record in DNS
  2. 2.Wrong DNS location - SPF record on wrong domain
  3. 3.Syntax error - Invalid SPF record format
  4. 4.Too many DNS lookups - Exceeding 10 lookup limit
  5. 5.Wrong IP included - Authorized IPs don't match sending servers
  6. 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

CheckCommandExpected
SPF record existsdig txt \grep spfRecord found
Single recordgrep -c spf1
Syntax validonline validatorPass
Sending IPs includedcompareIPs match
DNS lookupsonline validatorUnder 10
Propagationdig from multiple DNSSame 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 ```

  • [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)