Skip to content
Authentication

SPF record syntax: structure, rules, and examples

An SPF record is one TXT record with a strict grammar: version tag, qualified mechanisms, optional modifiers. The rules, the common shapes, and worked examples.

An SPF record is a single DNS TXT record published at the domain name it protects, beginning with the version tag v=spf1 and followed by a space-separated sequence of mechanisms and modifiers. The grammar is small and strict: receivers evaluate the terms left to right, stop at the first match, and treat malformed records as permanent errors. This entry covers the structural rules; the individual matching directives have their own entries.

The anatomy of a record

Every part labelledRFC 1035
TXT / SPFMX / PTRA / AAAACNAME / NS / other
v=spf1 ip4:198.51.100.7 ~include:_spf.esp.comMX-all
│ │ ││ │ │└ mechanism: all
│ │ ││ │ └ qualifier: - (fail)
│ │ ││ └ mechanism:MX(default + qualifier)
│ │ │└ mechanism: include with domain argument
│ │ └ qualifier: ~ (softfail) applied to the include
│ └ mechanism: ip4 with CIDR argument
└ version tag, mandatory, exactly once, first

Each term is a mechanism (a test against the connecting IP), optionally prefixed by a qualifier that sets the result if that mechanism matches. Omitting the qualifier means +, a pass. Evaluation is strictly first-match-wins: once a mechanism matches, its qualifier decides the outcome and nothing further is evaluated, which makes ordering meaningful.

The rules that break records when ignored

One record per domain

A domain must publish exactly one TXT record starting with v=spf1. Two SPF records is not a merged policy; it is a permerror, and receivers treat the domain as having no valid SPF at all. This happens constantly in practice when a second team or vendor adds their record instead of editing the existing one. Multiple sending services belong inside one record as multiple mechanisms.

Wrong and rightRFC 1035
TXT / SPFMX / PTRA / AAAACNAME / NS / other
; WRONG: two records = permerror
example.com.TXT"v=spf1 include:_spf.google.com -all"
example.com.TXT"v=spf1 include:sendgrid.net -all"
; RIGHT: one record, both services
example.com.TXT"v=spf1 include:_spf.google.com include:sendgrid.net -all"

Length and string splitting

A single TXT string is limited to 255 characters. Longer records are legal but must be split into multiple quoted strings inside the same record, which resolvers concatenate. Records long enough to need splitting are usually records with too many mechanisms, which points at a deeper problem: the 10 DNS lookup budget, covered in its own entry, is typically exhausted before the length limit becomes relevant.

Placement and scope

The record lives at the exact name being evaluated: SPF for mail.example.com lives at mail.example.com, and the record at example.com does not cover its subdomains. Every subdomain used in an envelope sender needs its own record, and every HELO hostname benefits from one too, since receivers may evaluate SPF against the HELO identity when the envelope sender is empty (bounces). The record type is TXT; the dedicated SPF record type (type 99) was deprecated years ago and should not be used.

Modifiers: redirect and exp

Besides mechanisms, the grammar allows modifiers, name=value pairs that may appear once each. redirect=domain.com replaces the entire evaluation with another domain's record and only takes effect if nothing else matched; it is occasionally useful for centralizing policy across many domains. exp=domain returns a custom explanation string on failure and is rarely worth its complexity. In practice, most records need neither, and a redirect combined with an all mechanism is a common contradiction: the all always matches first, so the redirect is dead code.

Worked examples

Common record shapesRFC 1035
TXT / SPFMX / PTRA / AAAACNAME / NS / other
; Small company: Google Workspace only, strict
"v=spf1 include:_spf.google.com -all"
; Own MTA plus ESP, strict
"v=spf1 ip4:198.51.100.0/24 include:_spf.esp.com -all"
; Sending subdomain for marketing (record at news.example.com)
"v=spf1 include:_spf.esp.com -all"
; Parked domain that never sends mail
"v=spf1 -all"
; Rollout phase: authorized senders pass, rest softfail while monitoring
"v=spf1 ip4:198.51.100.0/24 include:_spf.esp.com ~all"

After any edit, validate before and after publication: syntax checkers catch malformed grammar, and the testing and troubleshooting entry covers the tools and the failure vocabulary. DNS consoles that silently mangle quotes or split strings are a recurring source of records that look right in the editor and fail in resolution.

Frequently asked questions

Does the order of mechanisms matter?
Yes. Evaluation stops at the first matching mechanism. Put the mechanisms that match your highest-volume legitimate mail first for marginal efficiency, and always put all last, since it matches everything.
Can I put comments in an SPF record?
No. The grammar has no comment syntax. Documentation belongs in your DNS repository or infrastructure-as-code, next to the record definition.
Is v=spf1 case sensitive?
No, SPF terms are case insensitive, and v=spf1 by convention is written lowercase. Domains in arguments follow normal DNS case insensitivity.
What happens if my record has a typo?
Unknown or malformed terms make the whole record invalid, and receivers return permerror, which under DMARC counts as no SPF at all. Validate after every change; a single stray character fails the domain globally.

Key takeaways

  • One TXT record per domain, starting with v=spf1; a second SPF record produces permerror, not a merged policy
  • Terms evaluate left to right, first match wins, so all belongs at the end and ordering is meaningful
  • Records cover only the exact DNS name; every sending subdomain needs its own record
  • Strings over 255 characters must be split into quoted segments, but oversized records usually signal a lookup-budget problem
  • Modifiers redirect and exp exist and are rarely needed; a redirect after all is dead code