SPF explained: how to read, write and audit an SPF record

SPF explained: how to read, write and audit an SPF record

By James Bayliss · Updated · Reviewed

In this guide

What SPF actually checks

When a mail server connects to deliver a message, it announces an envelope sender with the SMTP MAIL FROM command. That address usually ends up in the message's Return-Path header. SPF, defined in RFC 7208, lets the owner of that envelope domain publish which IP addresses may use it.

Two details trip people up:

  • SPF checks the envelope domain, not the From: address people see. A newsletter platform often uses its own bounce domain in MAIL FROM, so SPF passes for their domain, not yours. That's why SPF passing doesn't guarantee DMARC passing; see DMARC alignment, explained.
  • SPF is evaluated against the IP that connected to the receiver. If mail is forwarded, the forwarder's IP connects, and SPF for the original domain usually fails. DKIM survives forwarding; SPF doesn't.

Anatomy of a record

Here is a typical record for an organisation that uses Microsoft 365 for staff mail and a marketing platform for newsletters:

example.com.  3600  IN  TXT  "v=spf1 include:spf.protection.outlook.com include:_spf.newsletter-vendor.example ip4:203.0.113.25 -all"

Read it left to right:

TermMeaning
v=spf1Version tag. Must be first, exactly once.
include:spf.protection.outlook.comEvaluate Microsoft's SPF record; if it returns pass, this term matches.
include:_spf.newsletter-vendor.exampleThe same for the marketing platform.
ip4:203.0.113.25This single IPv4 address matches.
-allEverything else fails.

Each term is a mechanism, optionally prefixed with a qualifier that sets the result when it matches:

QualifierResultTypical use
+ (default, usually omitted)passAuthorised senders
-fail-all at the end
~softfail~all while auditing, or with DMARC enforcement
?neutralRarely useful

The mechanisms

  • ip4: and ip6: match an address or CIDR range (ip4:198.51.100.0/24). They cost no DNS lookups, so they're the cheapest way to authorise stable infrastructure.
  • a and mx match the A/AAAA addresses of a host, or the addresses of a domain's MX hosts. Each costs one lookup, and mx can trigger up to 10 more address lookups behind the scenes.
  • include: evaluates another domain's SPF record and matches only if that record returns pass. It's how you authorise third-party senders.
  • exists: matches if a DNS name resolves. It's used with macros by large providers and rarely needed by anyone else.
  • ptr matches by reverse DNS. RFC 7208 says it should not be used: it's slow, and some receivers ignore it.
  • all always matches. It belongs at the end as the catch-all.

Two modifiers can also appear: redirect= (use another domain's record if nothing else matched) and exp= (an explanation string for failures). redirect= is useful for pointing many domains at one shared policy.

How receivers evaluate it

The receiver runs a procedure called check_host():

  1. Fetch TXT records at the envelope domain and keep those starting with v=spf1. None means the result is none; more than one is a permerror.
  2. Walk the mechanisms in order. The first one that matches decides the result, using its qualifier.
  3. If nothing matches and there's a redirect=, evaluate that domain instead. Otherwise the result is neutral.

Along the way, the receiver enforces hard limits. More than 10 terms that need DNS lookups (include, a, mx, ptr, exists, redirect) gives a permerror, and so do more than 2 lookups that return no data ("void lookups"). The too-many-lookups guide covers that limit in depth.

The all mechanism

The last term tells receivers what to do with everyone you didn't list.

  • -all (fail) says unlisted senders are not authorised. It's the clearest signal.
  • ~all (softfail) says they're probably not authorised. Many large senders use it deliberately. When DMARC is at p=quarantine or p=reject, DMARC drives the outcome anyway, and softfail avoids SPF-only rejections of forwarded mail by receivers that act on SPF directly.
  • ?all (neutral) gives receivers nothing to act on.
  • +all authorises the entire internet. It is never correct.

If you're unsure, use ~all while you discover senders, then decide between ~all and -all once DMARC is enforced. The difference matters far less once DMARC is in place than most debates suggest.

Writing a record from scratch

  1. Inventory every system that sends as your domain. Include the mail platform, marketing tools, the CRM, help desk, billing system, website contact forms, scanners and copiers, and monitoring alerts. DMARC aggregate reports are the best way to find the ones nobody remembers.
  2. Decide which ones should use your domain in the envelope. Many SaaS senders use their own bounce domain by default, which means they need DKIM alignment rather than an SPF entry. Check each vendor's documentation for "custom return-path" or "custom bounce domain".
  3. Add each sender using the vendor's published include, or ip4:/ip6: for your own servers.
  4. Count lookups. Each include counts as one, plus whatever that include contains.
  5. Publish one TXT record, then test it against a known sending IP with the SPF validator.

A domain that never sends mail should still publish a record, so spammers can't use it:

parked-example.com.  TXT  "v=spf1 -all"

Common mistakes

  • Two SPF records. Adding a second v=spf1 TXT record for a new service, instead of editing the existing one, makes every check a permerror. Merge them.
  • Over the 10-lookup limit. This is the most common cause of SPF failing after a new tool is added.
  • Using include: for a domain that has no SPF record. That's a permerror for every message that reaches that term.
  • Treating SPF as anti-spoofing on its own. SPF doesn't look at the visible From: address. Only DMARC ties authentication to the address people see.
  • Listing a vendor that doesn't use your envelope domain. It adds lookups and achieves nothing; that sender needs aligned DKIM instead.
  • Typos in the qualifier or mechanism. ip4:203.0.113.0/33, inlcude:, or a stray quote character all cause permerrors.
  • Splitting a long record incorrectly. DNS TXT strings are limited to 255 characters each, but one record can hold several strings. Receivers join them with no separator, so put the space inside the quotes: "v=spf1 ip4:192.0.2.1 " "include:_spf.example.net -all".

From the field: Three of the most frequent issues run into are: too many lookups, adding an email sending service and not adding it to the SPF, and having two SPF records. A Simple fix, but easily overlooked a lot of times. Remember: SPF is the list of servers authorized to send email on your behalf. You want to guard this closely for your company's email reputation.

Checking your record

The Email Record Lookup shows your record, its lookup count and any syntax problems. The SPF validator runs the full RFC 7208 evaluation for a specific IP, or for a whole CIDR range. That's useful when onboarding a new mail relay or confirming a provider's published range is covered.

Sources

Check it now

SPF Validation Is this IP address, or this whole range, allowed to send mail for the domain?

Related guides