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:

  1. You publish an SPF record: Add a single TXT record starting with v=spf1 on your domain (and on any subdomain that sends mail).
  2. An email is sent: A server with IP address 192.0.2.1 connects to the recipient's mail server and specifies a Return-Path domain (or HELO/EHLO domain).
  3. The receiving server checks your list: The receiver looks up the v=spf1 TXT record for that Return-Path domain and checks whether the sending server's IP address matches your authorized mechanisms.
  4. The result is passed to DMARC: If the IP matches, SPF returns Pass. If it does not match, SPF returns SoftFail (~all) or Fail (-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:

text
v=spf1 include:_spf.google.com ~all
  • v=spf1 identifies the TXT record as an SPF version 1 policy. Every SPF record must begin with v=spf1.
  • include:_spf.google.com authorizes 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:
    text
    v=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:

text
v=spf1 -all

Reputable, 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 one v=spf1 TXT record. Publishing two separate v=spf1 records causes SPF to fail immediately with PermError. Merge all include: and ip4:/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, and redirect) across the entire evaluation, including any nested lookups inside include: records. (all, ip4, and ip6 do 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.com for marketing or send.yourcompany.com for transactional receipts) gets its own independent 10-lookup budget.
    • Prefer ip4: and ip6: over a or mx for static servers you control.
  • Too many void lookups: the 2-void-lookup limit (PermError). RFC 7208 recommends failing with PermError if more than 2 DNS lookups return either an empty answer (0 records) or NXDOMAIN (non-existent domain). This usually happens when an include: or a: points to a decommissioned service or a typoed domain name.
  • Using +all or omitting all. Ending an SPF record with +all authorizes every server on the internet to impersonate your domain. Omitting all (without redirect=) leaves the default as ?all (Neutral). Always end your record with ~all or -all.
  • Unaligned Return-Path and email forwarding. Many third-party email tools use their own domain in the Return-Path unless you configure a custom return-path (or custom MAIL 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.
Was it helpful?