Back to Learning CenterEmail Authentication

How to validate SPF record syntax

By Samuel ChenardAugust 11, 20269 min read
How to validate SPF record syntax

An SPF record's syntax is valid when it is published as a DNS TXT record that begins with the exact version tag v=spf1, uses only the mechanisms and modifiers defined in RFC 7208, and stays inside the protocol's DNS lookup limits. A single misplaced qualifier, an unrecognized mechanism name, or a lookup count over 10 makes the whole record return a permanent error, or permerror, instead of a partial result. Validating syntax means checking the published record against this grammar before checking whether SPF is passing on real mail.

At a glance

Quick takeaways

  • SPF records must be published as DNS TXT (type 16) records only. The dedicated SPF resource record (type 99) was dropped from the protocol, so receivers do not need to query it.
  • A valid record must start with the exact version string v=spf1, ending at a space or the end of the record. A near match such as v=spf10 is not a valid SPF record.
  • The grammar defines exactly eight mechanisms: all, include, a, mx, ptr, ip4, ip6, and exists. A token outside that list breaks the grammar.
  • Any single syntax error anywhere in the record makes evaluation return permerror for the whole record, with no partial credit for the parts that were written correctly.
  • Terms that trigger a DNS query, include, a, mx, ptr, exists, and redirect, are capped at 10 total per evaluation. Exceeding that cap also returns permerror.
  • A domain must not publish more than one SPF record that an authorization check would select. Zero matching records returns "none"; more than one returns permerror.

How SPF syntax validation works

RFC 7208 defines SPF as a DNS-published policy that a receiving mail system evaluates through a function called check_host(). Validating syntax means confirming that a published record parses correctly under that function's grammar, before asking whether any particular message passes.

The record must exist as a TXT record. RFC 7208 Section 14.1 notes that a dedicated SPF resource record type (type 99) was defined early in the protocol's life but dropped, because migration to it was judged very unlikely. Publishing SPF as anything other than a TXT record is not part of the current specification.

Inside that TXT string, the record must open with a version section of exactly v=spf1. RFC 7208 Section 4.5 treats the version section as ending at the first space or at the end of the record, so a string that only starts with those characters but continues differently, such as v=spf10, is not recognized as SPF.

After the version tag, the grammar is closed. RFC 7208 Section 4.6.1 defines a record as a version followed by terms, where each term is either a directive (an optional qualifier plus one of the eight defined mechanisms) or a modifier (redirect, the explanation modifier exp, or an unrecognized name=value pair). Terms are separated by one or more spaces. The qualifier before a mechanism is one of +, -, ?, or ~, and defaults to + when omitted. The SPF hub covers how these mechanisms fit into a full record; this article stops at whether the grammar itself is well formed. For a term-by-term breakdown of mechanism and qualifier meaning, see SPF record syntax explained.

An unrecognized modifier is not automatically an error. RFC 7208 Section 6 states that unrecognized modifiers must be ignored no matter where or how often they appear. There is no equivalent tolerance for mechanisms in the grammar: a token that does not match one of the eight defined mechanism names, and does not match the modifier grammar either, falls outside every production in the record syntax. That gap is what the Section 4.6 evaluation rule turns into a permerror; it is not a separately quoted forgiveness clause the way unrecognized modifiers get.

When SPF syntax validation fails

Treat a record as failing validation, and stop before checking anything downstream, when any one of these conditions is true:

  • A syntax error exists anywhere in the record. RFC 7208 Section 4.6 states that check_host() returns permerror immediately on a syntax error, "without further interpretation or evaluation." A record with nine correct terms and one broken term is still a broken record.
  • More than one record is published. RFC 7208 Section 3.2 requires that a domain not publish multiple records that an authorization check would select. Zero matching records produces the result "none"; more than one produces permerror.
  • DNS-querying terms exceed 10. RFC 7208 Section 4.6.4 limits include, a, mx, ptr, exists, and redirect to 10 total per evaluation. ip4, ip6, and all do not count toward that limit, because they resolve without a DNS query. Each mx or ptr term is also separately capped at 10 address-record lookups.
  • Void lookups exceed the recommended limit. The same section states that implementations should limit lookups that return no answer, a "void lookup," to two before returning permerror.
  • A TXT character-string exceeds its bounds. RFC 7208 Section 3.3 caps a single TXT character-string at 255 octets. Longer records are split across multiple strings, which receivers concatenate without inserting spaces. Record content is US-ASCII.
A permerror is not a warning. RFC 7208 Section 8.7 describes permerror as meaning the published records "could not be correctly interpreted" and states the condition "definitely requires DNS operator intervention to be resolved." A receiver rejecting on permerror at SMTP time is expected to use reply code 550 with enhanced status code 5.5.2, so a broken record can turn into hard rejections at enforcement, not just a missed pass.

A worked SPF record example

The record below is illustrative only. Do not publish it as written; a real record must list the domain's actual sending sources, not a documentation example.

Technical exampletext
v=spf1 include:spf.provider.example ip4:203.0.113.0/24 -all

Reading it against the grammar:

  • v=spf1 is the mandatory version tag, present exactly once at the start.
  • include:spf.provider.example is a directive using the include mechanism. No qualifier is written before it, so it defaults to + (pass). include is a DNS-querying term and counts toward the 10-lookup limit.
  • ip4:203.0.113.0/24 is a directive using the ip4 mechanism with the reserved documentation range from RFC 5737. ip4 does not require a DNS query, so it does not count toward the lookup limit.
  • -all is a directive using the all mechanism with the - qualifier, requesting fail for anything that reaches this term without matching an earlier one. all also does not query DNS.
A record built from only these four terms uses one of the ten available DNS-querying lookups. A domain that chains several include mechanisms, each of which may itself contain further include or redirect terms, can reach the limit quickly, which is why the lookup count matters as much as the individual mechanism syntax.
SPF record mechanisms and DNS lookup costs
Source: Palisade.

What to check next

Work in this order once a record is in hand:

  • Query the domain's TXT records at the authoritative name server and at a second public resolver, and confirm exactly one record begins with v=spf1.
  • Read that record against the grammar above: version tag, only the eight defined mechanisms, valid qualifiers, and modifiers that are either redirect, exp, or an ignorable unrecognized name=value pair.
  • Count every include, a, mx, ptr, exists, and redirect term, including any reached through a chain of includes, and confirm the total is 10 or fewer.
  • If the record fails any of these checks, correct the DNS record directly. A permerror is a DNS-operator problem under RFC 7208, not something a receiver or sender application can work around.
SPF syntax is one input into the broader email authentication picture alongside DKIM and DMARC alignment; the email authentication overview covers how the three fit together, and what SPF is covers the mechanism at a higher level than the syntax rules here.

Check SPF syntax before troubleshooting delivery further

Run the domain through Palisade's SPF Record Checker to look up the published record, validate its syntax, and count its DNS lookups without registering an account. Do this after reading the record manually so a tool result confirms, rather than replaces, what the grammar rules above already show.

Check SPF

A syntax check confirms the published record parses correctly and counts its DNS lookups. It does not confirm that every legitimate sending source is included in the record, and it does not show whether a specific delivered message actually passed SPF; that requires the message's own authentication result.

Evidence

Sources and further reading

Questions readers ask

Frequently asked questions

Fix SPF limits without rebuilding your record

Start in Palisade.

Get started
Palisade domain settings with Hosted SPF enabled

Share this article

Samuel Chenard

Written by

Samuel Chenard

CEO & Co-Founder, Palisade

Samuel Chenard is the CEO and co-founder of Palisade, AI-first DMARC software for IT teams and MSPs, from one domain to thousands.

More from Samuel

Related articles