DEV Community

Cover image for SPF Records Explained: Prevent Email Spoofing with Sender Policy Framework
toolbox-poster
toolbox-poster

Posted on • Originally published at toolbox.starnomina.tn

SPF Records Explained: Prevent Email Spoofing with Sender Policy Framework

TL;DR
SPF (Sender Policy Framework) is the first line of defense against email spoofing, allowing domain owners to declare which IP addresses and servers are authorized to send mail on their behalf. Defined in RFC 7208, SPF has a deceptively simple syntax but hides critical constraints โ€” most notably the 10 DNS lookup limit that silently breaks authentication for complex sending infrastructures. This guide covers every mechanism, qualifier, and modifier in the SPF specification, walks through real-world lookup counting, and explains how SPF interacts with DMARC alignment.

๐Ÿ“‘ Table of Contents

  • How SPF Works
  • SPF Result Codes
  • Full Mechanism Reference
  • The 10-Lookup Limit
  • Real Lookup Count Examples
  • Provider Include Directives
  • SPF + DMARC Alignment
  • SPF Flattening Techniques
  • Best Practices
  • Common Mistakes
  • Tools
  • Sources & References

1. How SPF Works

When a receiving mail server accepts an incoming SMTP connection, it extracts the domain from the MAIL FROM command (the envelope sender, also called the Return-Path) and performs a DNS TXT lookup on that domain. If an SPF record is found, the receiver evaluates the connecting IP against the authorized mechanisms listed in the record.

๐Ÿ“– Definition โ€” SPF (Sender Policy Framework) is a DNS-based email authentication protocol (RFC 7208) that allows a domain owner to publish a list of IP addresses and hostnames authorized to send email using that domain as the envelope sender.

# SPF evaluation flow
1. Sender connects from IP 198.51.100.42
2. SMTP MAIL FROM: user@example.com
3. Receiver queries TXT record for example.com
4. Record: "v=spf1 ip4:198.51.100.0/24 include:_spf.google.com -all"
5. 198.51.100.42 matches ip4:198.51.100.0/24 โ†’ Result: PASS
Enter fullscreen mode Exit fullscreen mode

โš ๏ธ SPF does NOT validate the From: header that users see in their mail client. It validates the MAIL FROM (envelope sender / Return-Path). This is why DMARC alignment is required to connect SPF results to the visible sender identity.

2. SPF Result Codes

RFC 7208 ยง2.6 defines seven possible evaluation results:

Result Qualifier Meaning Typical Action
Pass + (default) IP is explicitly authorized Accept
Fail - IP is explicitly not authorized Reject or mark as spam
SoftFail ~ IP is probably not authorized (transitional) Accept but flag
Neutral ? Domain makes no assertion about this IP Accept
None โ€” No SPF record found Accept (no policy)
PermError โ€” Permanent error (syntax error, >10 lookups) Treat as fail or neutral (receiver-dependent)
TempError โ€” Temporary DNS error Try again later (4xx)

๐Ÿšซ PermError is silent and catastrophic. If your SPF record exceeds 10 DNS lookups, receivers return PermError โ€” which many treat as a fail. Your legitimate mail gets blocked or junked, and you receive no bounce notification.

3. Full Mechanism Reference

Mechanism DNS Lookups Syntax Description
ip4 0 ip4:198.51.100.0/24 Match IPv4 address or CIDR range
ip6 0 ip6:2001:db8::/32 Match IPv6 address or CIDR range
a 1 a or a:mail.example.com Match the A/AAAA record of the domain
mx 1 + MX lookups mx or mx:example.com Match the MX hosts' IPs
include 1 + nested include:_spf.google.com Evaluate another domain's SPF record recursively
exists 1 exists:%{i}.bl.example.com Pass if A record exists for macro-expanded domain
all 0 -all / ~all Catch-all โ€” always matches (must be last)
redirect 1 redirect=_spf.example.com Replace entire record with another domain's SPF
exp 1 exp=explain._spf.example.com Custom explanation string for failures

๐Ÿ’ก The ptr mechanism exists in the spec but RFC 7208 ยง5.5 explicitly discourages its use: "This mechanism SHOULD NOT be published." It is slow, unreliable, and counts as a DNS lookup.

4. The 10 DNS Lookup Limit

RFC 7208 ยง4.6.4 imposes a hard limit: SPF evaluation must not cause more than 10 DNS mechanisms/modifiers that require lookups. This includes include, a, mx, exists, redirect, and ptr. It does not include ip4, ip6, or all.

# These count toward the 10-lookup limit:
include:   โ†’ 1 lookup (+ any nested lookups from the included record)
a          โ†’ 1 lookup
mx         โ†’ 1 lookup (+ additional A lookups for each MX host)
exists     โ†’ 1 lookup
redirect   โ†’ 1 lookup
ptr        โ†’ 1 lookup (deprecated)

# These do NOT count:
ip4:       โ†’ 0 lookups (direct IP comparison)
ip6:       โ†’ 0 lookups (direct IP comparison)
all        โ†’ 0 lookups (catch-all terminator)
Enter fullscreen mode Exit fullscreen mode

10maximum DNS lookups allowed per SPF evaluation (RFC 7208 ยง4.6.4)

โš ๏ธ The limit is recursive. If include:_spf.google.com itself contains 3 include: directives, those 3 count toward your total of 10.

Void Lookup Limit

RFC 7208 ยง4.6.4 also defines a void lookup limit of 2. A void lookup is a DNS query that returns either NXDOMAIN or an empty answer. If more than 2 void lookups occur during evaluation, the result is PermError.

5. Real Lookup Count Examples

Here is a real-world example of how lookups accumulate for a domain using Google Workspace, Mailchimp, and SendGrid:

v=spf1 include:_spf.google.com include:servers.mcsv.net include:sendgrid.net -all

Lookup count breakdown:
โ”Œโ”€ include:_spf.google.com          โ†’ 1
โ”‚  โ”œโ”€ include:_netblocks.google.com  โ†’ 2
โ”‚  โ”œโ”€ include:_netblocks2.google.com โ†’ 3
โ”‚  โ””โ”€ include:_netblocks3.google.com โ†’ 4
โ”œโ”€ include:servers.mcsv.net          โ†’ 5
โ”‚  โ””โ”€ include:mcsv.net               โ†’ 6
โ”œโ”€ include:sendgrid.net              โ†’ 7
โ”‚  โ””โ”€ include:u12345.wl.sendgrid.net โ†’ 8
โ””โ”€ Total: 8 lookups โœ… (under 10)
Enter fullscreen mode Exit fullscreen mode

๐Ÿšซ Now add Salesforce (include:_spf.salesforce.com โ†’ 1) and Zendesk (include:mail.zendesk.com โ†’ 2 nested). That brings you to 11 lookups โ€” PermError. Your email breaks silently.

6. Provider Include Directives

Provider Include Directive DNS Lookups Used
Google Workspace include:_spf.google.com ~4
Microsoft 365 include:spf.protection.outlook.com ~2
SendGrid include:sendgrid.net ~2
Mailchimp include:servers.mcsv.net ~2
Amazon SES include:amazonses.com ~1
Salesforce include:_spf.salesforce.com ~1
HubSpot include:spf.hubspot.com ~2
Zendesk include:mail.zendesk.com ~3
Freshdesk include:email.freshdesk.com ~2

โšก Pro Tip: Before adding a new include:, always check its nested lookup count with an SPF checker tool. A single include can consume 1โ€“5 lookups depending on the provider's own SPF chain.

7. SPF + DMARC Alignment

For DMARC to consider SPF as a passing authentication method, two conditions must be met:

  1. SPF must evaluate to Pass for the MAIL FROM domain.

  2. The MAIL FROM domain must align with the From: header domain (relaxed or strict per DMARC's aspf tag).

โš ๏ธ Most third-party senders use their own Return-Path (e.g., bounces.sendgrid.net), which means SPF passes for their domain, not yours. SPF alignment fails โ€” DMARC must rely on DKIM alignment instead.

Custom Return-Path

To achieve SPF alignment with third-party senders, configure a custom return-path using your subdomain:

# Instead of:
Return-Path: bounces@em.sendgrid.net     โ†’ SPF passes for sendgrid.net (not aligned)

# Configure:
Return-Path: bounces@em.yourdomain.com   โ†’ SPF passes for yourdomain.com (aligned!)

# DNS: Add CNAME
em.yourdomain.com โ†’ u12345.wl.sendgrid.net
Enter fullscreen mode Exit fullscreen mode

8. SPF Flattening Techniques

When you hit the 10-lookup limit, SPF flattening replaces include: directives with their resolved ip4: and ip6: addresses, reducing lookups to zero for those entries.

# Before flattening (8 lookups):
v=spf1 include:_spf.google.com include:servers.mcsv.net include:sendgrid.net -all

# After flattening (0 lookups for Google IPs):
v=spf1 ip4:209.85.128.0/17 ip4:172.217.0.0/16 ip4:108.177.8.0/21
       include:servers.mcsv.net include:sendgrid.net -all
Enter fullscreen mode Exit fullscreen mode

๐Ÿšซ Flattening is fragile. Provider IPs change without notice. If Google adds a new IP range and your flattened record doesn't include it, mail from that IP fails SPF. You must automate flattening with a tool that re-resolves and updates your DNS record regularly.

๐ŸŽฏ A safer alternative to flattening is using the redirect= modifier to point to a dynamically managed SPF record on a subdomain, or using a service like Cloudflare's SPF management that handles flattening automatically.

9. Best Practices

End with -all
Use a hard fail (-all) in production. ~all (softfail) is for testing only; it lets unauthorized mail through.

Monitor lookup count
Check your total DNS lookup count after every change. A single vendor addition can push you over 10.

One record per domain
RFC 7208 ยง3.2: a domain must not have multiple SPF records. Two records cause PermError.

Avoid deprecated mechanisms
Never use ptr. It's slow, unreliable, and wastes a lookup. RFC 7208 explicitly discourages it.

Use ip4/ip6 for static IPs
Dedicated servers with fixed IPs should use ip4:/ip6: โ€” no lookup cost.

Keep under 255 characters per string
DNS TXT records are limited to 255-byte strings. Long records must be split into multiple strings within a single TXT record set.

10. Common Mistakes

๐Ÿšซ Multiple SPF records on the same domain. Publishing two TXT records that start with v=spf1 causes PermError (RFC 7208 ยง3.2). Merge them into one record.

๐Ÿšซ Exceeding the 10-lookup limit. This is the #1 SPF misconfiguration. Every include: adds lookups recursively. Use an SPF checker to validate the total.

โš ๏ธ Using +all. A record ending with +all authorizes the entire internet to send as your domain. This is equivalent to having no SPF record at all.

โš ๏ธ Forgetting the v=spf1 prefix. Without the version tag, the TXT record is not recognized as SPF. Receivers will return "None" (no SPF found).

โš ๏ธ Confusing include with redirect. include: evaluates another record and returns its result. redirect= replaces the entire evaluation with another domain's record. redirect cannot coexist with all.

โš ๏ธ Placing mechanisms after all. The all mechanism matches everything โ€” anything after it is never evaluated. Always put all last.

11. Tools

Validate and build your SPF records with these tools:

Tool Purpose
SPF Record Checker Validate syntax, count DNS lookups, detect errors
SPF Record Generator Build a syntactically correct SPF record by selecting providers

12. Sources & References

  • ๐Ÿ“„ RFC 7208 โ€” Sender Policy Framework (SPF) for Authorizing Use of Domains in Email

  • ๐Ÿ“„ RFC 7489 โ€” DMARC

  • ๐Ÿ“„ Google Workspace โ€” Authorize email senders with SPF

  • ๐Ÿ“„ Microsoft 365 โ€” Set up SPF to help prevent spoofing

  • ๐Ÿ“„ Cloudflare โ€” What is an SPF record?

  • ๐Ÿ“„ M3AAWG โ€” Best Practices for Implementing DMARC (includes SPF guidance)

๐ŸŽฏ Key Takeaway: SPF tells the world which IPs may send email as your domain โ€” but it only validates the envelope sender, not the visible From: header. Respect the 10 DNS lookup limit religiously, always end with -all, and pair SPF with DKIM so DMARC has a reliable alignment path that survives forwarding. Check your lookup count after every change.


Originally published on StarNomina ToolBox. Try our free online tools โ€” no signup required.

Top comments (0)