SPF Protocol Overview: Capabilities and Limitations

Understanding Sender Policy Framework (SPF), its limitations, and how it fits into modern email security with DMARC.

Sender Policy Framework (SPF) has been around for nearly two decades as a first line of defense against forged email senders. But is it enough on its own? And where does it fit into the modern email security landscape shaped by DMARC?

SPF matters because email spoofing is still the root cause of a massive chunk of phishing attacks. If you've ever wondered how someone could fake your boss's email or impersonate your company to your customers, you're about to find out how SPF helps—and where it still falls short.

Learning Objectives

  • Explain how SPF authenticates sending servers and the role DNS plays in the process
  • Describe the key limitations of SPF and why it can fail even for legitimate emails
  • Understand how SPF fits into DMARC and what it does (and doesn't) protect against

What is SPF?

SPF (Sender Policy Framework) is a DNS-based email authentication mechanism designed to prevent spoofing by verifying that a mail server is authorized to send email on behalf of a domain.

Think of SPF like a guest list for a VIP party. Only the people (IP addresses) listed are allowed in. If someone shows up not on the list, the bouncer (mail server) may turn them away—or at least flag them as suspicious.

SPF works by using a TXT record in DNS to publish a list of IP addresses or domains that are permitted to send email for your domain. When a receiving server gets an email claiming to come from [email protected], it checks the SPF record for yourdomain.com to see if the sender's IP is authorized.

The SPF specification is defined in RFC 7208 (2014 update of RFC 4408 from 2006), which provides the complete technical details of the protocol.

Why SPF is Important

Before SPF, anyone could pretend to be anyone else via email. SMTP, the email protocol invented in 1982, doesn't verify who's sending the message—it just takes their word for it. It's like handing a letter to the post office with someone else's name and return address and nobody checking your ID.

That trust-based design gave rise to massive problems:

  • Phishing emails appearing to come from banks, CEOs, or government agencies
  • Business Email Compromise (BEC) causing billions in losses annually
  • Spam campaigns hijacking reputations of well-known domains
  • Brand impersonation damaging company reputation and customer trust

SPF was one of the earliest attempts to fix that problem. By verifying sender IPs, it reduces the chance that an attacker can send email as your domain from an unauthorized source.

Critical Path-Based Limitation: SPF only validates the SMTP envelope sender (RFC5321.MailFrom), not the visible "From" header (RFC5322.From) that users see. This alignment gap allows attackers to pass SPF while spoofing trusted domains in headers.

How SPF Works

Let's walk through a simplified SPF check process:

1. DNS Record Publication

Domain owners publish an SPF record in their DNS zone file, specifying which servers are authorized to send email on their behalf.

2. Email Transmission

When an email is sent, the sending server connects to the receiving server and provides the envelope sender address (return-path).

3. SPF Lookup

The receiving server performs a DNS lookup for the SPF record of the domain in the envelope sender address.

4. IP Authorization Check

The receiving server compares the sending server's IP address against the authorized list in the SPF record.

5. Result Determination

Based on the comparison, SPF returns one of several results: Pass, Fail, SoftFail, Neutral, or TempError/PermError.

SPF Record Syntax and Examples

SPF records use a specific syntax defined in RFC 7208. Here's a basic example for a company using SendGrid:

example.com. IN TXT "v=spf1 include:sendgrid.net -all"

Complex Multi-Service SPF Record

# Enterprise SPF record with multiple services example.com. IN TXT "v=spf1 ip4:203.0.113.10/24 ip6:2001:db8::/32 include:_spf.google.com include:mailgun.org include:servers.mcsv.net mx:backup-mx.example.com -all" # Breakdown: # ip4:203.0.113.10/24 - Company's mail server subnet # ip6:2001:db8::/32 - IPv6 range for mail servers # include:_spf.google.com - Google Workspace # include:mailgun.org - Mailgun transactional email # include:servers.mcsv.net - Mailchimp marketing emails # mx:backup-mx.example.com - Backup MX server # -all - Hard fail for all others

SPF Record Size Optimization

Since DNS TXT records have size limits, complex SPF records may need optimization:

# Before optimization (approaching 512-byte limit) example.com. IN TXT "v=spf1 ip4:192.0.2.1 ip4:192.0.2.2 ip4:192.0.2.3 include:_spf.google.com include:mailgun.org include:sendgrid.net include:servers.mcsv.net mx -all" # After optimization using SPF delegation example.com. IN TXT "v=spf1 include:_spf1.example.com include:_spf2.example.com -all" _spf1.example.com. IN TXT "v=spf1 ip4:192.0.2.0/24 include:_spf.google.com include:mailgun.org" _spf2.example.com. IN TXT "v=spf1 include:sendgrid.net include:servers.mcsv.net mx"

Now when someone sends an email claiming to be from [email protected]:

  1. The recipient server checks the DNS record for yourdomain.com
  2. It sees that sendgrid.net is allowed to send on your behalf
  3. If the sending server's IP is listed under sendgrid.net, SPF passes
  4. If not, SPF fails and the message may be rejected or marked as spam

SPF Record Breakdown

Here's a more comprehensive SPF record example:

v=spf1 ip4:192.0.2.10 ip6:2001:db8::1 include:_spf.google.com mx -all

SPF Mechanisms Explained

  • v=spf1 - Version indicator (must be first)
  • ip4:192.0.2.10 - This specific IPv4 address is authorized
  • ip6:2001:db8::1 - Specific IPv6 address authorization
  • include:_spf.google.com - Trust Google's list of approved senders
  • mx - Authorize the domain's MX server IPs
  • a - Authorize the domain's A record IP

SPF Qualifiers

  • -all - Hard fail - only listed sources are allowed
  • ~all - Soft fail - treat non-listed sources as suspicious
  • ?all - Neutral - no policy (not recommended)
  • +all - Pass all - allows any IP (defeats SPF's purpose)

SPF Limitations and Weaknesses

While SPF provides valuable protection, it has several significant limitations:

1. Header From vs Envelope Sender Gap

SPF's fundamental weakness is path-based authentication - it only verifies the domain in the return-path (RFC5321.MailFrom), not the visible From header (RFC5322.From) that users see. This creates the authentication/authorization disconnect that led to DMARC development:

2. Email Forwarding Problems

SPF breaks with forwarding because forwarding server IPs aren't listed in the original domain's SPF record. Common scenarios through Gmail, Outlook, and Yahoo frequently break SPF validation for legitimate messages. This affects mailing lists, alumni email forwarding, and automatic forwards.

3. DNS Lookup Limits (RFC 7208 §4.6.4)

SPF processing is deliberately bounded: receivers must perform no more than 10 DNS-querying mechanisms/modifiers while evaluating a single message. This protects DNS, prevents infinite include/redirect loops, and avoids denial-of-service conditions. In plain terms: every time your SPF policy needs to “ask DNS another question,” it spends one of ten available tokens. Spend them wisely and you’ll be fine; overspend and the whole SPF check ends with a permanent error.

What counts toward the 10:

  • include: — counts as 1; mechanisms inside the included record are also evaluated and contribute to the same total
  • a — counts as 1, regardless of how many A/AAAA records exist
  • mx — counts as 1, regardless of how many MX targets (and their A/AAAA lookups)
  • exists: — counts as 1
  • redirect= — counts as 1; the target record’s mechanisms also contribute
  • ptr — counts as 1 (deprecated; avoid using)

Does not count: ip4:, ip6:, all, and the exp= explanation modifier.

Important nuance: the limit applies to the number of SPF terms that trigger DNS, not the raw number of DNS packets. For example, mx still counts as one even though it may involve several underlying A/AAAA queries for each MX host. Similarly, an include: counts once when referenced, but whatever that included record contains (e.g., more include, a, mx) continues consuming the same 10-term budget as evaluation proceeds. This is why vendor-provided SPF records can push you over the limit even when your top-level record looks simple—those includes unfold into more checks behind the scenes.

What happens if you hit the ceiling? The receiver should stop evaluation and return PermError for SPF. Under DMARC, a PermError does not count as an SPF “pass,” so alignment cannot succeed via SPF. If DKIM also fails (or is missing), DMARC enforcement will apply. In practice, this often looks like sporadic deliverability drops when a vendor quietly adds more includes to their own SPF, nudging your domain over the threshold without any change on your side.

How to stay comfortably under 10: plan a budget. Most organizations should aim for 8 or fewer counting terms to leave headroom for vendor changes. Prefer ip4:/ip6: ranges over include: where feasible, and consolidate third-party services behind your own delegated include (so you can curate and optimize once rather than everywhere). Regularly re-verify third-party SPF footprints during quarterly security reviews.

# Example: Easy to exceed 10 with nested includes v=spf1 include:_spf.google.com \ include:mailgun.org \ include:sendgrid.net \ mx a -all # Approximate counting (by SPF terms): # include:_spf.google.com -> 1 (plus the terms inside Google’s SPF) # include:mailgun.org -> 1 (plus Mailgun’s terms) # include:sendgrid.net -> 1 (plus SendGrid’s terms) # mx -> 1 # a -> 1 # If vendor records themselves have several includes, total evaluation can exceed 10. # Result when the limit is exceeded: SPF PermError per RFC 7208.

DNS Lookup Optimization Strategies

# Strategy 1: Replace includes with IP ranges # Instead of: include:service.com (may use 3-5 lookups) # Use: ip4:192.0.2.0/24 (0 lookups) # Strategy 2: Consolidate services # Create custom include record with multiple IPs _consolidated.example.com. IN TXT "v=spf1 ip4:198.51.100.0/24 ip4:203.0.113.0/24" example.com. IN TXT "v=spf1 include:_consolidated.example.com include:_spf.google.com -all" # Strategy 3: Use SPF macros for dynamic records example.com. IN TXT "v=spf1 include:%{ir}.%{v}._spf.example.com -all"

4. “Void” DNS Lookups (the two‑strike rule)

Not all DNS queries return useful data. SPF defines a void lookup as a DNS query that comes back with either NXDOMAIN (domain does not exist) or NOERROR with no answers. Think of this as knocking on a door where nobody lives—or where someone lives but refuses to answer. Two such “empty doors” across the whole SPF evaluation—counting your includes and redirects—trigger a stop condition. To keep resolvers healthy, RFC 7208 says that once you exceed two void lookups, evaluation must stop with a PermError.

Why this matters: void lookups are surprisingly easy to introduce. A small typo in an include: domain, a decommissioned vendor hostname, or an a/mx reference to a record that was cleaned up in DNS can silently accumulate voids. Because the rule is global to the entire evaluation (including nested includes), two misses anywhere can break otherwise healthy mail streams.

Operational impact and DMARC interaction: a void-induced PermError means SPF cannot pass, which removes SPF as a path to DMARC alignment. If DKIM isn’t present or fails to align, DMARC’s policy (quarantine/reject) can kick in. This is why you may see a sudden DMARC failure rate increase even though “nothing changed” in your main SPF record—an include target disappeared, and now you’re hitting the two‑void limit.

Typical causes of void lookups:

  • Typos or decommissioned vendors in include: (e.g., include:spf.mailgn.com)
  • a/mx/exists pointing at hostnames that don’t exist or have empty RRsets
  • Macro expansions that build non-existent domains (e.g., user-derived labels)

What it is not: timeouts or SERVFAIL are temporary errors and yield TempError, not a void lookup.

# Examples of void lookups # 1) include points at a non-existent domain v=spf1 include:_spf.no-such-vendor.tld -all # -> First query returns NXDOMAIN (void #1). A second miss elsewhere can trigger PermError. # 2) a:host.example.com where host.example.com does not exist v=spf1 a:host.example.com -all # -> NOERROR/NODATA or NXDOMAIN is a void lookup. Two such events => PermError.

Practical prevention: keep a simple preflight checklist. Before adding any new service to SPF, verify its include domain resolves and does not itself contain dead references; after adding, re-check your total counting terms and perform a spot test from a representative sending IP. Set calendar reminders to re-verify includes quarterly, and subscribe to vendor status feeds so you’re notified when they rotate infrastructure. Avoid ptr entirely; it’s deprecated, slow, and offers no security benefit.

5. Subdomain Inheritance Problems

SPF records don't inherit from parent domains, requiring separate SPF configuration for each subdomain. If example.com has an SPF record, mail.example.com doesn't automatically inherit it. This creates management overhead and security gaps.

Common SPF Problems and Solutions

❌ Problem: SPF passes, but the email still looks fake

This happens because SPF doesn't check the visible From address, only the return-path. Attackers exploit this by aligning the envelope sender with an allowed domain while showing a different "From" header to trick users.

Solution: Use DMARC, which requires SPF or DKIM and domain alignment with the visible From address.

📬 Problem: Legitimate emails fail SPF after forwarding

Email forwarding breaks SPF because the forwarder (like a mailing list or Gmail) uses its own IP, which you didn't authorize in your SPF record.

Solution: DMARC helps by allowing DKIM signatures to survive forwarding. ARC (Authenticated Received Chain, RFC 8617) preserves authentication through forwarding, now supported by Gmail, Outlook.com, and Yahoo.

🔄 Problem: Considering +all to avoid email failures

Using +all allows any IP to pass SPF—it's like putting "everyone welcome" on your guest list. This completely defeats SPF's purpose and opens the door to spoofing.

Best practice: Use -all (fail all unauthorized senders), or at least ~all if you're still testing and need to identify all legitimate sources.

🏗️ Problem: Subdomain email authentication

Each subdomain that sends email needs its own SPF record. Parent domain records don't automatically apply to subdomains.

Solution: Publish SPF records for each subdomain that sends mail, or configure the sp= tag in DMARC to manage subdomain policy inheritance.

SPF Validation and Testing

Command-Line Testing Tools

# Check SPF record syntax dig TXT example.com +short | grep "v=spf1" # Test SPF with specific IP and sender spfquery -i 203.0.113.45 -s [email protected] -h mail.example.com # Python SPF testing python3 -c " import spf result = spf.check2(i='203.0.113.45', s='[email protected]', h='mail.example.com') print(f'SPF Result: {result[0]} - {result[1]}') " # OpenSPF command line tool echo '[email protected]' | spfquery --ip=203.0.113.45 --helo=mail.example.com

SPF Record Validation

#!/bin/bash # SPF Record Validation Script DOMAIN="$1" echo "Validating SPF record for $DOMAIN..." # Check if SPF record exists SPF_RECORD=$(dig TXT "$DOMAIN" +short | grep "v=spf1") if [ -z "$SPF_RECORD" ]; then echo "❌ No SPF record found" exit 1 fi echo "📧 SPF Record: $SPF_RECORD" # Count DNS lookups LOOKUPS=$(echo "$SPF_RECORD" | grep -o 'include:\|a\|mx\|exists:' | wc -l) echo "🔍 DNS Lookups: $LOOKUPS/10" if [ "$LOOKUPS" -gt 10 ]; then echo "⚠️ WARNING: SPF record exceeds 10 DNS lookup limit" fi # Check for common issues if echo "$SPF_RECORD" | grep -q "+all"; then echo "❌ CRITICAL: SPF record allows all senders (+all)" elif echo "$SPF_RECORD" | grep -q "~all"; then echo "⚠️ NOTICE: SPF uses soft fail (~all) - consider hardening to -all" elif echo "$SPF_RECORD" | grep -q "-all"; then echo "✅ GOOD: SPF uses hard fail (-all)" else echo "⚠️ WARNING: No explicit all mechanism found" fi

Advanced SPF Testing Scenarios

# Test SPF with email forwarding scenario # Original sender: [email protected] via 203.0.113.10 # Forwarded through: gmail.com (74.125.0.0/16) # SPF check at original destination spfquery -i 203.0.113.10 -s [email protected] -h mail.company.com # Result: PASS # SPF check after forwarding spfquery -i 74.125.0.1 -s [email protected] -h mail.gmail.com # Result: FAIL (Gmail IP not in company.com SPF record) # This demonstrates why DKIM is important for forwarded emails

SPF Troubleshooting Guide

Common SPF Failures and Diagnostics

1. SPF PermError (Permanent Error)

# Causes of SPF PermError: # - Syntax errors in SPF record # - Exceeding 10 DNS lookup limit # - DNS resolution failures # - Void DNS lookups (>2) # Diagnosis commands: dig TXT example.com +short | grep spf spfquery -i 203.0.113.1 -s [email protected] --debug # Fix examples: # Before: v=spf1 include:badservice.com -all (badservice.com doesn't exist) # After: v=spf1 include:goodservice.com -all

2. SPF TempError (Temporary Error)

# Causes of SPF TempError: # - DNS server timeouts # - Temporary DNS resolution failures # - Network connectivity issues # Diagnosis: nslookup example.com 8.8.8.8 dig TXT example.com @1.1.1.1 +trace # Typically resolves automatically, but may indicate: # - DNS server overload # - Network connectivity issues # - DNS configuration problems

3. SPF None vs. Neutral vs. Fail

# SPF Result Meanings: NONE # No SPF record published NEUTRAL # SPF record exists but makes no assertion (?all) PASS # Sender IP is authorized FAIL # Sender IP explicitly denied (-all) SOFTFAIL # Sender IP not authorized but policy is lenient (~all) # DMARC treats these differently: # PASS -> Can satisfy DMARC SPF alignment (with domain alignment) # FAIL -> Cannot satisfy DMARC (policy applies) # SOFTFAIL -> Treated as FAIL for DMARC purposes # NONE -> Cannot satisfy DMARC SPF alignment # NEUTRAL -> Cannot satisfy DMARC SPF alignment # TEMPERROR/PERMERROR -> Cannot satisfy DMARC

SPF Best Practices

  • Phased deployment: Start with ~all, monitor for 30 days, then move to -all
  • DNS lookup optimization: Keep total lookups under 8 for safety margin
  • Regular auditing: Quarterly review of all email sending sources
  • Subdomain policies: Explicitly deny subdomains that don't send email
  • Documentation: Maintain inventory of all authorized sending services
  • Monitoring: Use DMARC reports to track SPF authentication results
  • Backup strategies: Plan for service migrations and IP changes
  • Testing: Validate SPF records in staging before production deployment

SPF Security Hardening

# Security-focused SPF configurations # Deny all subdomains that don't send email *.example.com. IN TXT "v=spf1 -all" # Explicit subdomain policies marketing.example.com. IN TXT "v=spf1 include:servers.mcsv.net -all" support.example.com. IN TXT "v=spf1 include:zendesk.com -all" # Use strict IP ranges instead of broad includes where possible # Instead of: include:broadservice.com # Use: ip4:203.0.113.0/28 (specific subnet) # Monitor for SPF record tampering # Set up DNS monitoring alerts for SPF record changes

SPF's Role in DMARC

SPF is only one piece of the email authentication puzzle. While it's great at verifying servers, it's terrible at verifying who the message appears to come from. That's where DMARC comes in:

  • Identifier alignment - DMARC requires the domain that passes SPF to align with the visible From header (relaxed or strict)
  • Policy enforcement - DMARC standardizes handling with none/quarantine/reject policies
  • Aggregate reporting - XML reports provide daily visibility into SPF authentication results
  • Either/or authentication - DMARC passes if either SPF or DKIM passes with alignment, providing redundancy
  • Closes the gap - Addresses SPF's header/envelope disconnect that enabled 84.2% of pre-DMARC phishing

Next Steps

SPF is an essential foundation for email security, but it's not sufficient on its own. To build a comprehensive email authentication strategy:

  1. Implement SPF properly - Start with ~all, identify all sources, then move to -all
  2. Add DKIM signatures - Learn how DKIM adds cryptographic signatures to verify message integrity
  3. Deploy DMARC - Understand how DMARC enforces alignment between authentication results and the visible From header
  4. Monitor and iterate - Use DMARC reports to continuously improve your email authentication posture

Remember: SPF is one of the easiest wins in email security, but don't stop there. The real power comes from combining SPF with DKIM and DMARC to create a comprehensive defense against email spoofing and phishing.

Learn More

To complete your understanding of email authentication, explore these related guides:

Critical: SPF alone cannot prevent email spoofing! Attackers can still spoof the visible From header while passing SPF checks. Always implement SPF as part of a comprehensive DMARC deployment for effective domain protection.

Common Mistakes: Using +all (allows anyone), exceeding 10 DNS lookups, forgetting subdomain policies, and assuming SPF alone prevents spoofing. Test thoroughly before enforcing -all policies.