What Is an SPF Record? Syntax, Limits and Failure Modes

Summary

What is an SPF record? It is a single DNS TXT record, starting with v=spf1, that lists which servers may send mail using your domain in the envelope sender. Receivers compare the connecting IP against it and return pass, fail or softfail. It is capped at 10 DNS lookups, breaks on forwarding, and only helps DMARC when the Return-Path domain matches your From domain.

Server room corridor with network racks and a patch panel in focus

What is an SPF record? It is a single DNS TXT record on your domain that lists which servers are allowed to send mail using that domain in the envelope sender. A receiving server reads it, compares the connecting IP against the list, and returns pass, fail, softfail or neutral. It authenticates the sending path, not the message, and it says nothing about the From address a person reads.

That distinction explains most SPF tickets we see in traces.

An SPF record is an allowlist of senders, published in DNS

SPF stands for Sender Policy Framework, specified in RFC 7208. The record is a TXT entry at the root of the domain that starts with v=spf1. A minimal one looks like this:

example.com.  IN  TXT  "v=spf1 ip4:203.0.113.10 include:_spf.provider.example ~all"

Read it left to right. The receiver checks each term in order and stops at the first match. The final all term catches everything that did not match earlier.

The domain being checked is not the From header. It is the domain in the SMTP MAIL FROM command, also called the envelope sender or Return-Path. If that is empty, as on bounces, the receiver falls back to the HELO name.

Developer desk with a laptop terminal and a hand-drawn network diagram notebook

The mechanisms: ip4, ip6, a, mx, include, exists

Each term is a mechanism, optionally prefixed by a qualifier. Six cover nearly every real record.

Qualifiers set the result when a term matches. + is pass and is the default, - is fail, ~ is softfail, ? is neutral. So -all means "reject anything not listed" and ~all means "accept but flag".

Prefer ip4: and ip6: whenever you control the address. They cost zero lookups and cannot break when someone else's DNS changes.

The 10-lookup limit is the constraint that breaks real records

RFC 7208 caps a single SPF evaluation at 10 terms that trigger DNS queries: include, a, mx, ptr, exists and the redirect modifier. Nested includes count. Past 10, the result is permerror, and the check fails permanently rather than falling back.

There is a second limit that gets less attention. Terms that return an empty answer or NXDOMAIN are void lookups, and the spec says to stop at two. A stale include: pointing at a vendor you dropped last year is a void lookup you are paying for.

Here is how teams actually hit the ceiling. Google Workspace takes one include. A transactional provider takes one. A marketing platform takes one, and it nests three more inside. A support desk, a CRM and a billing tool add four. You are at 11 without having done anything unusual.

The failure is silent. Mail still sends, and some receivers still deliver it, but SPF no longer authenticates anything for you. If you rely on DMARC alignment through SPF, that path is gone until you flatten the record or remove senders.

Three fixes work, in order of preference:

  1. Remove senders you no longer use. Audit includes against your last 90 days of DMARC aggregate reports.

  2. Move bulk traffic to a subdomain such as mail.example.com with its own SPF record and its own 10-lookup budget.

  3. Flatten includes into literal ip4: ranges, but only with automation. Vendors rotate IPs, and a hand-flattened record goes stale without any alert.

Whiteboard sketch of arrows from several sending services to one central box

Publish exactly one SPF record per domain

A domain must not publish two SPF records. If a receiver finds two v=spf1 TXT entries, the result is permerror, same as the lookup limit. This happens when one person adds a record for a new tool while another already has one in place.

The fix is mechanical. Merge the terms into a single record, with one v=spf1 at the start and one all at the end. Everything the vendor documentation tells you to add goes inside the existing record as another include:, never as a new TXT entry.

Two smaller traps. A TXT string over 255 characters must be split into multiple quoted strings inside the same record, which most DNS providers do for you. And keep the whole response under roughly 450 bytes where you can, since larger answers may fall back from UDP to TCP and some resolvers handle that badly.

Softfail or hardfail: pick based on what you can see

The all term is a policy statement, and the default advice ("use -all") skips the part that matters. A hard fail is only safe when you know every legitimate sender.

Our rule of thumb from reading traces:

In practice the difference is smaller than people expect. Most large receivers treat SPF as one signal among several, and with a DMARC policy in place the DMARC verdict is what decides. Still, a -all on an incomplete record is how you lose invoices.

Labeled ethernet cable bundle in a server rack

SPF breaks on forwarding, and that is by design

SPF validates the IP that connected to the receiving server. When a message is forwarded, a mailing list redistributes it, or an alias service relays it, the connecting IP is the forwarder's, not yours. The SPF check fails even though you did nothing wrong.

You cannot fix this inside SPF. That is the reason DKIM exists alongside it: a signature survives the hop, an IP list does not. A domain that depends on SPF alone will see authentication failures on every forwarded message.

It is also why SPF alone does not satisfy DMARC for forwarded mail. Plan for DKIM to carry those messages.

SPF alignment: passing is not the same as matching your From domain

A message can pass SPF and still fail DMARC. DMARC needs the domain that passed SPF, the Return-Path domain, to match the visible From domain. Many shared providers use their own bounce domain by default, so SPF passes for provider-mail.net and says nothing about example.com.

Check this in the raw headers. In the Authentication-Results line, look at smtp.mailfrom=. If it is not your domain or a subdomain of it, SPF is passing without helping you.

The remedy is a custom Return-Path on your own subdomain, which most serious senders support. Postmark, Resend, Mailgun and SendGrid all document this setup, though it is not always on by default.

Inbox providers now expect authentication from every sender

Since February 2024, Google requires all senders to authenticate with SPF or DKIM, and senders of more than 5,000 messages a day to Gmail must have SPF, DKIM and DMARC in place. The details are in Google's email sender guidelines. Yahoo set matching rules at the same time.

This moved SPF from "nice to have" to a delivery prerequisite. A missing or broken record now shows up as rejections and spam placement, not just a lower reputation score.

Our read after a couple of years of traces: the rules mostly catch domains with no authentication at all and senders with high spam complaint rates. A correct SPF record by itself will not get you into the inbox, but a missing one will keep you out.

How to check and fix an SPF record in four steps

Do these in order, and re-check after each DNS change.

  1. Look up the current record. Run dig +short TXT example.com and find the entry starting v=spf1. If there are two, merge them.

  2. Count lookups. Add up include, a, mx, exists, redirect, and everything nested in the includes. If you are at 8 or higher, plan the cleanup now, not at the 11th.

  3. List real senders. Use DMARC aggregate reports or Authentication-Results headers from test sends. Add only the sources that appear.

  4. Send a test and read the header. The result should read spf=pass with smtp.mailfrom on your domain.

DNS changes take time to propagate, and the TTL on your current record decides how long old answers survive. Lower it to 300 seconds a day before a planned change.

A worked record for a SaaS company with three senders

Take a product that sends from Google Workspace, a transactional provider and a support desk. A clean record looks like this:

example.com.  IN  TXT  "v=spf1 include:_spf.google.com include:spf.transactional.example include:spf.helpdesk.example -all"

That is three includes, and each one hides its own nested lookups. Count them before you publish, because the visible length tells you nothing about the real budget. A record with three includes can already sit at 7 or 8 lookups.

Now split by purpose. Keep human mail on the root domain. Put product notifications on notify.example.com with a record that only lists the transactional provider. The root record stays short, the subdomain gets its own 10-lookup budget, and a reputation problem on one stream no longer drags the other.

This split also makes DMARC reports readable. Each stream has its own Return-Path domain, so a failure points at one sender instead of a pile of them.

What SPF will not do

It will not stop someone spoofing your From address, because it never looks at that header. It will not encrypt anything. It will not tell you why a message landed in spam. And it will not scale past the 10-lookup budget without ongoing maintenance.

Treat it as one of three records you publish together: SPF for the sending path, DKIM for message integrity, DMARC for the policy that ties them to your From domain. If you have SPF in place and nothing else, the next concrete step is publishing a DMARC record at p=none and reading the reports for two weeks.

Frequently asked questions

What is an SPF record in simple terms?
It is a DNS TXT record that lists the servers permitted to send email for your domain. Receiving servers check the sending IP against that list and decide whether the message passes SPF.
How many SPF records can a domain have?
One. Publishing two records that both start with v=spf1 causes a permerror. Merge every authorized sender into a single record with one all term at the end.
What is the SPF 10 DNS lookup limit?
RFC 7208 caps evaluation at 10 DNS-querying terms: include, a, mx, ptr, exists and redirect. Nested includes count. Going over returns permerror and SPF stops authenticating your mail.
Should I use -all or ~all in my SPF record?
Use ~all while you inventory senders and switch to -all once DMARC reports show every legitimate source passing for several weeks. Never use +all, which authorizes any server.
Why does SPF fail when email is forwarded?
SPF checks the IP that connects to the receiver. A forwarder connects from its own IP, which your record does not list. DKIM survives forwarding because the signature travels with the message.
Does SPF alone protect against spoofing?
No. SPF validates the envelope sender, not the visible From address. To protect the From domain you need DMARC, which requires SPF or DKIM to pass and align with it.
notificationharbor
Start free