DEV Community

Cover image for SPF Record Syntax: Mechanisms & Limits | FortifyNet
Håkan Fägnell
Håkan Fägnell

Posted on Originally published at fortifynet.com

SPF Record Syntax: Mechanisms & Limits | FortifyNet

An SPF record is a single DNS TXT record, published at your domain's apex, that begins with v=spf1, lists the mail servers authorized to send on your behalf, and ends with an all mechanism telling receivers what to do with everything else. v=spf1 include:_spf.google.com ~all is a complete, valid record. Every other piece of SPF record syntax is a variation on those three parts - plus a set of hard limits that silently break records which otherwise look correct.

Those limits matter more than most people expect. In a scan of 5,499,028 domains from the Tranco list carried out on 27 February 2026, DMARCguard found that 56.0% publish an SPF record - ahead of DMARC (30.4%) and DKIM (22.7%) - but that 4.8% of those SPF records, 148,655 domains, exceed the 10-lookup ceiling set by RFC 7208 and therefore fail evaluation outright.

A terminal window showing DNS TXT record output, illustrating how an SPF record is published and read

The three parts of every SPF record

Every valid record breaks down the same way:

v=spf1  ip4:203.0.113.10  include:_spf.google.com  -all
  |            |                    |               |
version    mechanism            mechanism      catch-all
Enter fullscreen mode Exit fullscreen mode
  1. The version tag. v=spf1 must be the first term and must match exactly. Anything else means the record is not an SPF record at all.
  2. Mechanisms. Evaluated strictly left to right. The first mechanism that matches the connecting IP decides the result and evaluation stops immediately - order is not cosmetic.
  3. The catch-all. An all mechanism as the final term. Anything published after all is ignored.

One thing worth being precise about: SPF checks the envelope sender used in the SMTP MAIL FROM command (the Return-Path) and the HELO identity. It does not check the From: header your recipient actually sees. Closing that gap is the job of DMARC, which is why the two are always deployed together. If you are still mapping out the wider setup, our DNS security configuration guide covers how SPF, DKIM, DMARC and DNSSEC fit together.

SPF once had a dedicated DNS record type (type 99). RFC 7208 §3.1 retired it in April 2014 - publish SPF as a TXT record only.

SPF mechanisms: the full reference

Mechanism Matches when the sending IP… DNS lookups Notes
all always matches 0 Must be the last term
ip4: falls inside the given IPv4 address or CIDR range 0 ip4:203.0.113.0/24 - costs nothing against the lookup limit
ip6: falls inside the given IPv6 address or prefix 0 ip6:2001:db8::/32
a matches an A or AAAA record of the domain 1 Bare a means the current domain; a:mail.example.com names another
mx matches an address record of one of the domain's MX hosts 1 Each MX host must not resolve to more than 10 address records
include: passes the included domain's own SPF evaluation 1, plus whatever the included record itself spends The most-used mechanism and the usual cause of lookup blowouts
exists: the expanded domain name has any A record 1 Used with macros for per-sender rules; rare in ordinary records
ptr reverse DNS for the IP resolves back into the domain 1 RFC 7208 §5.5 states plainly: "This mechanism SHOULD NOT be published." Remove it

A subtlety that trips people up: include: is not a textual paste. It runs a full, separate SPF evaluation against the included domain and matches only if that evaluation returns pass. A -all inside an included record does not reject your mail - it simply makes the include: not match, and evaluation continues to your next mechanism.

Qualifiers: four characters that decide the verdict

Any mechanism can carry a qualifier prefix. RFC 7208 §4.6.2 defines exactly four, and the default is +.

Qualifier Result What it asks the receiver to do When to use it
+ (default) pass Treat the sender as authorized Implicit - +mx and mx are identical
~ softfail Accept, but mark as suspicious ~all during rollout, or where forwarding is common
- fail Treat as unauthorized; reject or bin -all once every legitimate sender is listed
? neutral Make no statement either way Practically the same as publishing nothing

Never publish +all. It authorizes the entire internet to send as your domain and is strictly worse than having no SPF record.

Modifiers: redirect and exp

Modifiers are name/value pairs rather than mechanisms, and they may appear only once each.

  • redirect=example.com replaces your record's result wholesale with the target domain's SPF result. It costs one DNS lookup and is ignored entirely if an all mechanism is present, so the two should not appear together.
  • exp=explain.example.com points at a TXT record supplying a human-readable explanation string returned on a fail. It costs no lookup at evaluation time.

The limits that break otherwise-valid records

This is where most real-world SPF failures live. All of the following come straight from RFC 7208 §3.4 and §4.6.4:

  • 10 DNS-querying terms. The include, a, mx, ptr and exists mechanisms and the redirect modifier each consume one. Exceeding 10 MUST return permerror - a hard failure, not a warning. all, ip4, ip6 and exp are free.
  • 2 void lookups. A query returning NXDOMAIN or an empty answer is a "void lookup". Implementations should cap these at two; exceeding the cap also produces permerror. Stale include: entries for services you no longer use are the usual culprit.
  • 10 address records per mx or ptr. Beyond the overall budget, each individual MX host is capped at 10 A/AAAA records.
  • One record per domain. If a lookup returns more than one v=spf1 record, the result is permerror. Merge them into a single record; never publish two.
  • 255 characters per string, 450 octets recommended overall. A TXT record can hold multiple quoted strings, concatenated without spaces. RFC 7208 §3.4 recommends keeping the whole DNS answer under 450 octets so it fits in a UDP packet.
  • A 20-second evaluation budget. Receivers should allow at least 20 seconds and return temperror past that.

Where your ten lookups actually go

Each third-party sender you add spends at least one lookup, and some spend more because their own records nest further includes.

Sender Typical include: Lookups consumed
Google Workspace _spf.google.com 1
Microsoft 365 spf.protection.outlook.com 1
Mailchimp servers.mcsv.net 1
SendGrid sendgrid.net 1
Amazon SES amazonses.com 1
Zendesk mail.zendesk.com 1
Salesforce exacttarget.com 2

Six or seven services and you are at the ceiling. The fixes, in order of preference: remove includes for services you have retired, move senders onto dedicated subdomains with their own SPF records, and only then consider flattening includes into literal ip4: ranges - flattening removes lookups but means you must track your providers' IP changes yourself.

SPF adoption by TLD, February 2026

Share of domains publishing an SPF record, by top-level domain. Source: DMARCguard, "State of Email Authentication 2026", scan of 5,499,028 Tranco domains, 27 February 2026.

Five syntax mistakes that cause PermError

  1. Two v=spf1 records on the same name. Common after a migration. Merge, do not stack.
  2. A qualifier on all that contradicts intent - ?all looks cautious but tells receivers nothing, so spoofed mail sails through.
  3. Terms after all. Everything to the right of all is dead text.
  4. Leftover ptr. Deprecated since 2014, slow, and it burns a lookup for no benefit.
  5. Includes for retired vendors. They cost a lookup each and can turn into void lookups when the vendor removes the record.

How to check your own record

Read your record straight from DNS with a single query:

dig +short TXT example.com
Enter fullscreen mode Exit fullscreen mode

On Windows, the equivalent is nslookup -type=TXT example.com. Look for exactly one string starting with v=spf1, then count the lookup-consuming terms by hand - and remember to count the ones hidden inside each include:.

Since February 2024, Google and Yahoo have required bulk senders (roughly 5,000 messages a day or more) to authenticate with both SPF and DKIM and to publish a DMARC record of at least p=none. A permerror from a broken SPF record puts that compliance at risk, so it is worth verifying rather than assuming.

Run a free 60-second FortifyNet scan and we will check your SPF record alongside DKIM, DMARC, your TLS configuration, security headers and dark-web exposure - no signup needed.

Frequently asked questions

Can a domain have two SPF records?
No. If a lookup returns more than one v=spf1 record, RFC 7208 §4.5 requires receivers to return permerror, which fails SPF entirely. Combine the mechanisms into one record.

Should I use -all or ~all?
Start with ~all while you confirm every legitimate sender is listed, then move to -all. -all is the stronger anti-spoofing signal, but only once your inventory of senders is genuinely complete.

Do ip4: entries count toward the 10-lookup limit?
No. ip4:, ip6:, all and exp cause no DNS queries at evaluation time and are exempt. Only include, a, mx, ptr, exists and redirect count.

Does SPF on its own stop spoofing?
No. SPF validates the envelope sender, not the From: header a recipient reads. An attacker can pass SPF on a domain they control while displaying yours. DMARC ties the two identities together, which is what actually closes the gap.

How long can an SPF record be?
Each individual string in a TXT record is capped at 255 characters, though multiple strings are concatenated. RFC 7208 recommends keeping the whole DNS answer under 450 octets so it survives in a UDP packet.

Related guides


Sources: RFC 7208, Sender Policy Framework (SPF) version 1, IETF, April 2014 (§3.1, §3.4, §4.5, §4.6.2, §4.6.4, §5.5, §6.1, §6.2) · DMARCguard, "State of Email Authentication 2026", scan of 5,499,028 Tranco domains, 27 February 2026 · Google and Yahoo bulk sender requirements, effective February 2024.

Originally published at fortifynet.com/blog/spf-record-syntax. I'm the founder of FortifyNet, a website security scanner; this article comes from our blog, so factor in that founder bias when you read any tool recommendations here.

Top comments (0)