Email Security
SPF: who can send emails for you
How SPF works, how to configure your SPF record using your email provider's documentation, full mechanism syntax, and how to fix common SPF problems.
SPF (Sender Policy Framework, defined in RFC 7208) is a public guest list of the servers and services allowed to send email on behalf of your domain.
Standard email (SMTP) does not verify the sender address on its own. Without SPF and DKIM, an attacker can send messages that appear to come from your domain. Publishing an SPF record protects your domain from impersonation, improves deliverability, and is one of the building blocks required for DMARC and your BIMI inbox logo.
We strongly recommend configuring an SPF record for every domain you own, and always using your email provider's official documentation to get the exact value they require.
How SPF works#
When a mail server receives a message, it checks the domain in the message's Return-Path (also called the MAIL FROM or bounce address) rather than the visible From: header shown in the inbox:
- You publish an SPF record: Add a single TXT record starting with
v=spf1on your domain (and on any subdomain that sends mail). - An email is sent: A server with IP address
192.0.2.1connects to the recipient's mail server and specifies aReturn-Pathdomain (orHELO/EHLOdomain). - The receiving server checks your list: The receiver looks up the
v=spf1TXT record for thatReturn-Pathdomain and checks whether the sending server's IP address matches your authorized mechanisms. - The result is passed to DMARC: If the IP matches, SPF returns
Pass. If it does not match, SPF returnsSoftFail(~all) orFail(-all), and DMARC decides whether to quarantine or reject the message.
Because SPF checks the Return-Path domain while readers see the From: header, DMARC also checks alignment: the Return-Path domain must match your From: domain (or be a subdomain of it) for SPF alone to satisfy DMARC.
How to configure an SPF record#
An SPF record must always be published as a DNS TXT record (the older dedicated SPF DNS record type was retired by RFC 7208). At the root of your domain (@), a typical record looks like this:
v=spf1 include:_spf.google.com ~allv=spf1identifies the TXT record as an SPF version 1 policy. Every SPF record must begin withv=spf1.include:_spf.google.comauthorizes Google Workspace's sending servers. If you use more than one service (for example Google Workspace for team mail and Postmark for product emails), combine them into the same TXT record:textv=spf1 include:_spf.google.com include:spf.mtasv.net ~all~all(SoftFail) or-all(Fail) at the very end tells receiving servers that no other IP addresses are authorized to send mail for the domain.
Domains and subdomains that never send email#
If you own parked domains, or subdomains like www that never send email, publish a strict no-mail SPF record on them so attackers cannot spoof them:
v=spf1 -allReputable, developer-friendly email providers#
Always check the official documentation of each service that sends email for you, copy their recommended include: mechanism or custom return-path setup, and add it to your domain's DNS settings.
Postmark#
| Field | Value |
|---|---|
| Typical SPF setup | Custom Return-Path CNAME (pm-bounces) or include:spf.mtasv.net |
| Official guide | Postmark SPF & Return-Path guide |
Resend#
| Field | Value |
|---|---|
| Typical SPF setup | SPF TXT record on a dedicated sending subdomain (send) |
| Official guide | Resend domain verification docs |
Amazon SES#
| Field | Value |
|---|---|
| Typical SPF setup | Custom MAIL FROM subdomain with v=spf1 include:amazonses.com ~all |
| Official guide | Amazon SES SPF authentication |
SendGrid#
| Field | Value |
|---|---|
| Typical SPF setup | Automated security CNAMEs (or include:sendgrid.net) |
| Official guide | SendGrid domain authentication |
Mailgun#
| Field | Value |
|---|---|
| Typical SPF setup | include:mailgun.org on your sending domain or subdomain |
| Official guide | Mailgun domain verification |
Google Workspace#
| Field | Value |
|---|---|
| Typical SPF setup | include:_spf.google.com |
| Official guide | Google Workspace SPF setup |
Microsoft 365#
| Field | Value |
|---|---|
| Typical SPF setup | include:spf.protection.outlook.com |
| Official guide | Microsoft 365 SPF setup |
Technical reference: SPF syntax, mechanisms & results#
Receiving servers evaluate an SPF record from left to right. The first mechanism that matches the connecting server's IP address determines the result. If no mechanism matches and no redirect= modifier is present, the default result is Neutral (as if ?all were at the end).
Qualifiers#
Every mechanism can be prefixed with one of four qualifiers. When omitted, + (Pass) is the default.
| Qualifier | SPF result | Meaning |
|---|---|---|
+ (default) |
Pass |
The host is authorized to send mail. |
- |
Fail |
The host is not authorized (for example -all). Receivers may reject the message. |
~ |
SoftFail |
The host is not authorized, but the domain is in transition (~all). Receivers accept and mark as suspicious, or defer to DMARC. |
? |
Neutral |
Makes no assertion about whether the host is authorized (?all). Treated the same as None. |
Mechanisms#
| Mechanism | Uses DNS lookup? | What it matches |
|---|---|---|
all |
No | Matches all IP addresses. Placed at the very end (~all or -all) as the default rule; any mechanism after all is ignored. |
include:<domain> |
Yes | Evaluates the SPF record of <domain>. If that evaluation returns Pass, the include: matches. A -all or ~all inside the included record does not fail your outer record; evaluation simply moves to the next term. If <domain> has no SPF record, it triggers a PermError. |
ip4:<ip-or-cidr> |
No | Matches a single IPv4 address (ip4:192.0.2.1, default /32) or CIDR block (ip4:192.0.2.0/24). |
ip6:<ip-or-cidr> |
No | Matches a single IPv6 address (default /128) or IPv6 CIDR block (ip6:2001:db8::/32). |
a or a:<domain> |
Yes | Matches the A (IPv4) or AAAA (IPv6) records of the current domain or <domain> (optional /cidr). |
mx or mx:<domain> |
Yes | Matches the IP addresses of the MX servers for the current domain or <domain> (capped at 10 address lookups per mx term). |
exists:<domain> |
Yes | Performs an A record lookup on <domain> (often combined with macros); matches if any A record exists. |
ptr (deprecated) |
Yes | Reverse DNS lookup. Do not use: RFC 7208 deprecates ptr because it is slow, unreliable, and adds heavy DNS load. |
Modifiers#
Modifiers are optional name=value pairs that may appear at most once per record.
| Modifier | Uses DNS lookup? | What it does |
|---|---|---|
redirect=<domain> |
Yes | Replaces the record with <domain>'s SPF policy when no earlier mechanism matches. Useful when one organization shares a single SPF policy across several domains. Ignored if all is present in the record. |
exp=<domain> |
No | Specifies a domain whose TXT record provides a custom SMTP rejection string when SPF returns Fail. Rarely used. |
SPF evaluation results#
| Result | Meaning | Typical action |
|---|---|---|
Pass |
The sending IP is authorized by the SPF record. | Accept (and pass SPF for DMARC if aligned). |
Fail |
The sending IP is explicitly unauthorized (-all). |
Reject (550 5.7.1) or apply DMARC policy. |
SoftFail |
The sending IP is probably unauthorized (~all). |
Accept and mark as spam, or apply DMARC policy. |
Neutral |
The record makes no assertion (?all or no all term). |
Treat as unauthenticated. |
None |
No SPF record exists on the domain, or the domain is invalid. | Treat as unauthenticated. |
TempError |
Transient DNS error (timeout or SERVFAIL). |
Temporarily defer (451) and retry later. |
PermError |
Permanent error in the published SPF record (syntax error, multiple SPF records, >10 lookups, or >2 void lookups). | Fails SPF authentication until you fix the DNS record. |
Common issues#
- Multiple SPF records on the same domain (
PermError). A domain or subdomain may only have onev=spf1TXT record. Publishing two separatev=spf1records causes SPF to fail immediately withPermError. Merge allinclude:andip4:/ip6:entries into a single record. Note that long TXT records (>255 characters) split into multiple quoted strings inside a single DNS TXT record are concatenated automatically without spaces; make sure there is a space between terms at string boundaries. - Too many DNS lookups: the 10-lookup limit (
PermError). RFC 7208 allows at most 10 DNS-querying mechanisms and modifiers (include,a,mx,ptr,exists, andredirect) across the entire evaluation, including any nested lookups insideinclude:records. (all,ip4, andip6do not count toward this limit.) To stay under 10 lookups:- Remove
include:statements for old tools and services you no longer use. - Use separate subdomains for different mail streams. Each subdomain (such as
mail.yourcompany.comfor marketing orsend.yourcompany.comfor transactional receipts) gets its own independent 10-lookup budget. - Prefer
ip4:andip6:overaormxfor static servers you control.
- Remove
- Too many void lookups: the 2-void-lookup limit (
PermError). RFC 7208 recommends failing withPermErrorif more than 2 DNS lookups return either an empty answer (0records) orNXDOMAIN(non-existent domain). This usually happens when aninclude:ora:points to a decommissioned service or a typoed domain name. - Using
+allor omittingall. Ending an SPF record with+allauthorizes every server on the internet to impersonate your domain. Omittingall(withoutredirect=) leaves the default as?all(Neutral). Always end your record with~allor-all. - Unaligned
Return-Pathand email forwarding. Many third-party email tools use their own domain in theReturn-Pathunless you configure a custom return-path (or customMAIL FROM) subdomain. In addition, when a recipient auto-forwards an email to another inbox, the forwarder's IP address often fails the original sender's SPF check. Configuring DKIM alongside SPF ensures your emails still pass DMARC even when forwarded.