Email was designed in the 1970s, when the network was a few hundred computers run by people who knew each other. SMTP — the protocol mail servers still use to hand messages around — has no built-in way to verify that a server claiming to send mail from ceo@yourcompany.com is actually allowed to send mail from that domain. You typed in whatever sender you wanted, and the receiving server trusted it. That trust, at internet scale, is the entire reason spam and phishing exist.

SPF is the first of three layered protocols that try to fix this. The other two are DKIM, which adds a cryptographic signature, and DMARC, which ties everything together with a policy. SPF on its own isn't sufficient for modern email security — but everything else builds on it, and the most common reason a domain's email fails authentication is something wrong in the SPF record. So this is where to start.

What an SPF record actually is

An SPF record is a DNS TXT record published at the root of your domain. There's no special record type — just a TXT record whose value begins with v=spf1. When a mail server receives a message claiming to come from @yourdomain.com, it looks up yourdomain.com's TXT records, finds the one starting with v=spf1, and walks through it to decide whether the IP that just connected is allowed to send for your domain. If you're new to DNS, the DNS records primer covers the broader picture; the DNS lookup tool shows every record type for any domain.

The record itself is a single line of text. It's a version identifier, followed by a sequence of mechanisms that declare which senders are authorised, followed by a fallback all mechanism that says what to do about everything else. Here's a typical record:

v=spf1 include:_spf.google.com ip4:203.0.113.0/24 -all

That single line tells the receiving server: this domain sends through Google Workspace, plus any IP in the 203.0.113.0/24 range. Anything else, reject. Read piece by piece:

  • v=spf1 — the version identifier. Always present, always first. There's only one defined version of SPF and there has been since 2006.
  • include:_spf.google.com — delegate to Google's SPF record. Whatever IPs Google authorises in _spf.google.com are also authorised for this domain. This is how Google Workspace customers don't have to maintain a list of Google's outbound mail server IPs by hand.
  • ip4:203.0.113.0/24 — explicitly authorise any IP in that range. Useful for your own mail servers, or for a third-party sender that publishes their IPs but doesn't have an SPF record you can include.
  • -all — the catchall. The - qualifier means hard fail: anything not matched by an earlier mechanism should be rejected.

Most real-world SPF records look exactly like this — a version, a couple of include: entries for the services that send mail on the domain's behalf, sometimes an explicit IP range, and an all at the end. The complexity comes from how many includes accumulate as a business adds senders.

The mechanisms

SPF defines a handful of mechanisms that can appear between v=spf1 and the final all:

  • include:domain — recursively evaluate another domain's SPF record. If that record authorises the sending IP, the include matches.
  • ip4:addr or ip4:addr/prefix — authorise a specific IPv4 address or CIDR block.
  • ip6:addr/prefix — same, for IPv6.
  • a or a:domain — authorise whatever IPs the A record of the domain resolves to.
  • mx or mx:domain — authorise the IPs of the domain's MX (mail exchange) servers. Convenient on the assumption that the servers that receive your mail also send mail, which isn't always true.
  • exists:domain — match if the given domain resolves to anything at all. Rarely seen in practice.
  • all — the catchall, with a qualifier in front.

There's also a ptr mechanism in the original SPF spec, which checks the reverse DNS of the connecting IP. RFC 7208 explicitly deprecates it: it's slow, unreliable, and dangerous on shared hosting. Don't use it.

The qualifiers

Every mechanism can be prefixed with a qualifier that tells the receiver what to do when that mechanism matches:

  • +pass. Authorised. This is the default if no qualifier is written, which is why you usually see include:_spf.google.com rather than +include:_spf.google.com.
  • -hard fail. Explicitly not authorised. Receivers should reject the message.
  • ~soft fail. Not authorised, but receivers should accept the message and treat it suspiciously (usually delivering to spam). Most senders use this while rolling SPF out and haven't migrated to -all when they should.
  • ?neutral. The sender takes no position. Effectively the same as having no SPF record at all for that path.

The qualifier on the final all mechanism is the one that matters most. -all says "reject anyone not on my list." ~all says "accept it but flag it." +all says anyone can send as me — which is the configuration equivalent of leaving the front door unlocked with a sign that says "free money inside." It happens more often than you'd think, usually when someone copies an example from a tutorial without reading what it means.

The 10-DNS-lookup limit

This is the single most important detail about SPF in practice. RFC 7208 §4.6.4 caps the number of DNS-consuming mechanisms in a single SPF evaluation at ten. include, a, mx, ptr, exists, and the redirect modifier each count as one. The ip4 and ip6 mechanisms cost nothing, because the receiver already has the IPs without doing any lookup. So the limit is on how many indirections the receiver has to chase.

Why ten? Because every include: can recursively expand into more includes, every a can resolve multiple addresses, and a malicious or careless record can fan out enough lookups to amplify into a DoS against the receiving server. The limit is a hard ceiling.

What happens if you exceed it: the receiver returns permerror, which the SPF spec defines as "the domain's published records could not be correctly interpreted." In effect, SPF fails for every message from that domain — not just the one being checked, every single one. Receivers that respect DMARC's p=reject policy will reject the lot of them. Big-providers (Google, Microsoft, Yahoo) all enforce this strictly. The failure mode is silent from the sender's perspective: your mail server keeps sending happily, and an unknown but growing fraction of recipients quietly bounce.

This is the number-one cause of "we suddenly can't deliver to Gmail." Someone added Salesforce. Then HubSpot. Then a new analytics vendor. Each one is an include: that itself might contain four or five nested includes. You can land on 11 lookups without realising it. GitHub's published SPF sits at exactly 10 of 10 by design — that's how close to the edge well-resourced operators run.

The SPF record checker resolves the whole include tree for any domain, counts every lookup, and tells you where you stand against the limit. If you administer a domain, the only safe way to add a new sender is to check the lookup count before and after.

Common mistakes

The same handful of misconfigurations come up over and over:

  • +all — already mentioned, but worth repeating because it appears on more domains than it should. It declares that any IP on the internet is allowed to send mail as your domain. Anyone can perfectly spoof you.
  • Too many includes. Google Workspace, Microsoft 365, Mailchimp, Salesforce Marketing Cloud, HubSpot, SendGrid, and Constant Contact each typically cost two to four lookups once their nested includes are expanded. Three or four of them and you're at the limit. The fix is either SPF flattening (replacing includes with the IPs they currently resolve to, which has its own maintenance cost) or simply removing senders you don't actually use any more.
  • The ptr mechanism. Deprecated for over a decade. If you see it in your record, take it out.
  • No SPF record at all. Modern receivers don't treat this as catastrophic on its own, but combined with no DKIM and no DMARC it's the configuration of an obvious phishing target. Spammers will start using your domain as the From: address on their messages because nothing stops them.
  • Multiple SPF records. A domain may publish only one v=spf1 record. Publishing two — usually because someone added a new sender by creating a new TXT record instead of editing the existing one — causes permerror.

SPF alone isn't enough

Even a perfectly configured SPF record doesn't stop email spoofing on its own. The reason is a subtle one: SPF authenticates the envelope sender — the MAIL FROM address the sending server gives the receiver during the SMTP handshake — which is also what appears in the Return-Path header. It does not authenticate the From: header that your mail client actually displays.

Those two are usually the same, but they don't have to be. A spammer can perfectly pass SPF using their own domain in MAIL FROM — they control that domain, so they can publish whatever SPF they like for it — while putting ceo@yourcompany.com in the visible From: header. The receiver checks SPF, sees a pass, and delivers a spoofed message into the inbox.

SPF authenticates the envelope. DKIM signs the message. DMARC ties both to the From: header your user actually sees.

That's why all three exist. DKIM adds a cryptographic signature over the headers and body, so the receiver can verify the message hasn't been modified in transit and was signed by someone with the right private key. DMARC layers on top and tells the receiver to check that either SPF or DKIM authenticated a domain that aligns with the From: header — closing the spoofing gap that SPF alone leaves wide open.

The DMARC checker grades your domain's full email-authentication posture and is the next thing to check after SPF. If you want to trace a specific message through every server that touched it, the email header analyzer parses the raw headers and shows the auth-results in plain English. And we'll have a companion post on DKIM next.

Try it

Check your SPF record

Paste any domain to see its full SPF record, walk the include tree, count DNS lookups against the 10-lookup ceiling, and get a plain-English read on whether anything is going to break.

Check your SPF record →