To set up an SPF record, list every server and service that sends mail as your domain, combine them into one DNS TXT record at the domain root that starts with v=spf1 and ends with -all (or ~all while you are still checking), and publish it. Then test it with dig and a real message. That is the whole job; the rest of this guide is about getting each part right.

SPF (Sender Policy Framework, RFC 7208) lets a receiving server check whether the IP that delivered a message is allowed to send for the domain in the envelope sender (the MAIL FROM, also called the Return-Path). It does not look at the visible From header. That gap is why SPF is only one of three pieces, alongside DKIM and DMARC.

We use example.com and the documentation IP 203.0.113.10 throughout.

Step 1: Inventory everything that sends as your domain

This is the step people skip, and it is where most broken SPF records come from. Check:

  • Your own mail server, if you run one.
  • The service your app uses for password resets and receipts.
  • Your newsletter or marketing tool.
  • Your helpdesk, CRM, invoicing and calendar tools, if they send "from" your domain.
  • Your office mailbox provider.

For each one, find out whether it sends with your domain in the envelope sender or with its own. Many providers use their own bounce domain by default, in which case their SPF include belongs on that domain, not yours. Their documentation will tell you which, and usually gives the exact include: value.

If you are not sure what is sending, look at a few real messages: open them in Gmail, choose "Show original" and read the Return-Path and Authentication-Results headers. Our email checker also shows what your current DNS already says.

Step 2: Build the record

An SPF record is a list of mechanisms read left to right. The first match wins.

Mechanism Meaning Counts toward 10 lookups?
ip4:203.0.113.10 This IPv4 address (or range, e.g. /29) may send No
ip6:2001:db8::/32 This IPv6 range may send No
a The IPs in this domain's A record may send Yes
mx The IPs of this domain's MX hosts may send Yes
include:spf.provider.example Whatever that domain's SPF allows may send Yes, plus its own lookups
exists, ptr, redirect= Advanced; ptr is discouraged by RFC 7208 Yes
-all / ~all Everything else fails / softfails No

For a domain that only sends from its own server, this is our real record shape:

v=spf1 ip4:203.0.113.10 -all

If your app also sends through a provider:

v=spf1 ip4:203.0.113.10 include:spf.provider.example -all

Prefer ip4: for servers you control. It is exact and costs no DNS lookups. Use include: for providers, because their IP ranges change and they maintain the list for you.

-all or ~all?

-all means "anything not listed is not authorized". ~all means "probably not authorized, treat with suspicion". Start with ~all if you are unsure your inventory is complete, watch your DMARC reports for a couple of weeks, then switch to -all. We use -all on our own domains. Never use +all, which authorizes the entire internet.

Step 3: Publish it in DNS

In your DNS provider, create (or edit) a TXT record:

Field Value
Type TXT
Host / Name @ (the domain root)
Value v=spf1 ip4:203.0.113.10 -all
TTL 3600 is fine; lower it to 300 while you are testing

Most DNS panels add the quotation marks for you. If yours shows the value in quotes after saving, that is normal. If you typed quotes and it now shows doubled quotes, remove yours.

If an SPF record already exists, edit it. Do not add a second one.

Add SPF for your mail server's hostname too

If you run your own server, bounce messages and some system mail use the server's hostname itself as the envelope sender, for example MAILER-DAEMON@mail.example.com, or an empty sender with mail.example.com as the HELO name. Receivers then check SPF for mail.example.com. We publish this on the mail host:

Type Host Value
TXT mail v=spf1 a -all

a means "the IP in this host's own A record", which is the server itself. Our self-hosted email server guide covers the rest of that DNS set.

Subdomains that never send

A subdomain without an SPF record has no SPF policy, which is not the same as "may not send". For a domain that should never send mail at all, publish:

v=spf1 -all

Do the same for parked domains you own but never send from. It stops them being used in spoofed envelope senders, and it costs nothing. Pair it with a DMARC record of v=DMARC1; p=reject on those domains.

SPF record examples for common setups

These cover most of the domains we look at. Replace the include values with the ones your providers document.

Situation Record
Own mail server only v=spf1 ip4:203.0.113.10 -all
Own server plus an app email provider v=spf1 ip4:203.0.113.10 include:spf.provider.example -all
Two servers, one IP range v=spf1 ip4:203.0.113.10 ip4:203.0.113.16/29 -all
Office mailbox provider plus newsletter tool v=spf1 include:spf.office.example include:spf.newsletter.example ~all
Domain that never sends v=spf1 -all

A common pattern for providers that send in bulk is to give them their own subdomain, for example news.example.com, with its own SPF record. That keeps the root record short and separates the reputation of marketing mail from your transactional mail. SPF too many DNS lookups explains how that also solves the lookup limit.

If you want one record to reuse another's policy entirely, redirect= does that: v=spf1 redirect=_spf.example.com. It is useful when many domains share one sender list. It counts as a lookup, and it is ignored if the record also contains an all mechanism.

Step 4: Understand how SPF fits with DMARC

DMARC passes on SPF only if SPF passes and the envelope sender's domain aligns with the From header domain. If your newsletter tool sends with its own bounce domain, SPF may pass for that domain while doing nothing for yours. That is fine as long as DKIM is signed with your domain. Most providers offer a "custom return path" or "custom bounce domain" option if you want SPF alignment too.

With aspf=s (strict, which we use) the domains must match exactly. With the default relaxed mode, bounce.example.com aligns with example.com.

What the SPF results mean

When you read Authentication-Results headers or a verifier report, you will see one of these results, defined in RFC 7208:

Result Meaning Usual cause
pass The IP is authorized Working as intended
fail The IP is explicitly not authorized (-all) A missing sender, or spoofing
softfail Probably not authorized (~all) A missing sender, or spoofing
neutral The domain makes no assertion (?all) A deliberately vague record
none No SPF record found The record is missing or on the wrong host
temperror A temporary DNS problem A DNS timeout; usually resolves itself
permerror The record cannot be evaluated Two records, a syntax error, or more than 10 lookups

permerror is the one to fix urgently. Receivers may treat it like a fail, and DMARC treats it as no SPF pass at all.

Common mistakes

  • Two SPF records. Adding a second v=spf1 TXT record when a new service asks you to "add this SPF record" causes a permerror, and SPF fails for every message. Merge them into one.
  • More than 10 DNS lookups. Every include, a, mx, ptr, exists and redirect counts, including the ones inside included records. Go over 10 and the result is permerror. See SPF too many DNS lookups for how to count and fix it.
  • Forgetting a sender. A forgotten helpdesk or invoicing tool fails SPF the day you switch to -all. Do the inventory, then watch DMARC aggregate reports before tightening.
  • Using ptr. RFC 7208 discourages it: it is slow, costs lookups and is unreliable. Use ip4: instead.
  • Putting the record on the wrong host. Some panels append the domain automatically, so typing example.com as the host creates example.com.example.com. Use @ for the root.
  • Expecting SPF to survive forwarding. It does not, unless the forwarder uses SRS. That is why we run postsrsd on our own forwarding server, and why DKIM matters.

How to verify it worked

Query the record directly:

dig +short TXT example.com
dig +short TXT mail.example.com

You should see exactly one string starting with v=spf1 for each name. To check it through a public resolver, which is what many receivers effectively see, add @1.1.1.1:

dig +short TXT example.com @1.1.1.1

Then send a real message from each of your senders to port25's verifier. If you run your own server, swaks makes this quick:

swaks --to check-auth@verifier.port25.com --from hello@example.com \
  --server mail.example.com --port 587 --tls \
  --auth LOGIN --auth-user hello@example.com

The reply lists the SPF result; you want pass. For a provider, send through it normally to the same address. You can also send to mail-tester.com or a Gmail account and check "Show original" for spf=pass in Authentication-Results.

If SPF passes but mail still goes to spam, the cause is somewhere else. Our why are my emails going to spam checklist walks through the rest.

Want someone to audit and fix SPF, DKIM, DMARC and PTR for your domain on whatever provider you use? That is our Deliverability Fix; prices are on the pricing page.