Learn

What is SPF?

SPF (Sender Policy Framework) is a DNS record that lists the servers allowed to send email for your domain. Here is how receivers use it, what each part of a record means, and where providers trip people up.

How SPF works, end to end

Every email travels with two sender addresses. One is the From line people see. The other is the envelope sender, given to the receiving server in the SMTP MAIL FROM command before the message itself is sent; receivers usually record it as the Return-Path header, and it is where bounces go. SPF, defined in RFC 7208, is about the envelope sender, not the From line.

  1. A sending server connects to the receiver and announces itself with HELO or EHLO.
  2. It gives the envelope sender, for example bounces@example.com. The receiver now knows two things: the connecting IP address and the domain example.com.
  3. The receiver looks up the TXT records of example.com and keeps the one that starts with v=spf1.
  4. It evaluates the record's terms from left to right against the connecting IP, fetching more DNS records where a term asks for them. The first term that matches decides the result.
  5. The result goes into the message's authentication headers, and the receiver combines it with everything else it knows to decide on delivery. The RFC itself doesn't require any particular action for any result.

Bounce messages have an empty envelope sender. For those, RFC 7208 checks postmaster@ plus the name the server gave in HELO, so a server that sends bounces needs SPF on its own host name too. Receivers may also check the HELO name on its own.

SPF on its own never looks at the From line, which is why it can't stop someone from putting your domain there. That is DMARC's job: under RFC 9989, an SPF result only helps a message pass DMARC when it is a pass and the envelope domain aligns with the From domain.

The seven results

ResultWhat it means
passThe domain explicitly authorizes the connecting server.
failThe domain explicitly says the server is not authorized. Comes from -all or another - term.
softfailA weaker statement: the server is probably not authorized. Comes from ~all.
neutralThe domain makes no statement about this server. Comes from ?all, or from a record with no all that matched nothing.
noneThere was no SPF record to check, or no usable domain to check it for.
temperrorA temporary error, usually DNS. Trying again later may work.
permerrorThe record can't be interpreted, for example because of two records or too many lookups. Someone has to fix the DNS.

Anatomy of a record

Here is the record of a domain that sends from one office server, Google Workspace and Microsoft 365. Real records rarely need all three, but each part shows one idea:

v=spf1 ip4:203.0.113.10 include:_spf.google.com include:spf.protection.outlook.com ~all
PartWhat it does
v=spf1Marks the TXT record as SPF. It has to come first, exactly like this.
ip4:203.0.113.10Authorizes one IPv4 address, here an office server. A range like /24 works too, and ip6: does the same for IPv6.
include:_spf.google.comPass if Google's own SPF record passes for this IP. This is how Google Workspace's servers get authorized.
include:spf.protection.outlook.comThe same for Microsoft 365's servers.
~allEverything that didn't match gets softfail. The ~ is the qualifier; - would make it fail.

Other mechanisms exist. a and mx authorize the servers behind your own A or MX records, exists is for advanced setups built on macros, and the redirect= modifier hands the whole decision to another domain's record. ptr is still in the standard, but the RFC says not to publish it. Every mechanism, all included, can carry a qualifier: + pass, which is the default, - fail, ~ softfail or ? neutral. Modifiers such as redirect= can't.

An include is not a copy and paste. The receiver evaluates the other domain's record on its own and only asks whether it passed. Even a -all inside the included record doesn't end the check of yours; it just means "not one of this provider's servers", and the receiver moves on to your next term.

A worked example

Say example.com publishes the record above and three messages arrive at a receiver, all with From: example.com.

An employee writes from Gmail in Google Workspace. The envelope sender is alice@example.com, so the receiver fetches the SPF record of example.com. The connecting IP is not 203.0.113.10, so ip4 doesn't match. Next comes include:_spf.google.com: the receiver fetches Google's record, which may point to further records of its own, and finds the IP in one of Google's ranges. The include matches, its qualifier is the default +, and the result is pass. The Microsoft include and ~all are never looked at. Because the envelope domain is the From domain, DMARC can count this pass.

A newsletter goes out through a mailing service. Many services use their own bounce address, for example bounce-7f2@mailer.example.net. The receiver now checks the SPF record of mailer.example.net, not yours, and it passes because the service lists its own servers. Your record isn't involved at all. SPF passes, but for a domain that doesn't align with example.com, so DMARC can only pass this message through a DKIM signature made with your domain.

Someone forges your domain from their own server. The envelope says example.com, the receiver walks the whole record, nothing matches, and ~all gives softfail. Whether the message is delivered now depends on the receiver and on your DMARC policy. A record ending in -all would have produced fail instead.

Notice what the forger's message cost: the receiver had to resolve every include to be sure nothing matched. That worst case is the one the DNS lookup limit is about.

The rules that break records

Three rules cause most SPF failures, and each has its own article. Evaluating a record may take at most 10 DNS lookups, counting every include nested inside other includes, and going over turns SPF into a permanent error: how to fix SPF "too many DNS lookups" shows how to count and cut them.

A domain may publish only one SPF record, and a second one breaks both: multiple SPF records explains how to merge them.

And the ending matters less than people think once DMARC is in place: SPF softfail vs fail compares ~all and -all, and why +all must never be used.

Provider gotchas

  • Google and Microsoft disagree on the ending. Google recommends ~all for Google Workspace, while Microsoft recommends -all for Microsoft 365, because it expects DKIM and DMARC to be set up as well. Both work; pick one and stick to it.
  • "Up to 10 includes" is not the whole story. Google's guide says a record can have up to 10 include: tags. The limit actually counts lookups, and Microsoft's guide spells out that nested includes add to it, so fewer than 10 includes doesn't guarantee fewer than 10 lookups.
  • Subdomains don't inherit SPF. Microsoft notes that each domain or subdomain that sends mail needs its own SPF record. A record on example.com says nothing about mail whose envelope sender is news.example.com; DMARC, by contrast, does cover subdomains.
  • Some services never use your SPF record. Amazon SES, for one, sends with a subdomain of amazonses.com as the envelope sender unless you set up a custom MAIL FROM domain. Until you do, adding SES to your own record changes nothing, and SPF can't align for DMARC.
  • Domains that never send still deserve a record. Microsoft suggests v=spf1 -all for parked domains, which tells receivers that no server may send for them.
  • Long records get split. A TXT string holds at most 255 characters. Longer values are split into several quoted strings in the same record, never into two records.

Check yours

The SPF checker follows every include, counts the lookups and shows each finding with its fix. To start from scratch, the SPF generator builds one record for all the services you send with.

SPF setup guides by service

Mailbox providers

Marketing platforms

Transactional email

Help desks

Related tools and articles

We use Google Analytics cookies only if you accept. Privacy policy