Blog/ Deliverability & authentication

SPF Record Syntax Explained: Mechanisms, Qualifiers, Modifiers

Nafiul HasanNafiul Hasan· 10 min read
AI Emaily blog cover for SPF record syntax, showing an SPF TXT record broken into its mechanisms, qualifiers and modifiers

The short answer

An SPF record is a DNS TXT string that starts with v=spf1. Its mechanisms — ip4, ip6, a, mx, include, exists, ptr and all — list where mail may come from. Each carries a qualifier: + (pass), - (fail), ~ (softfail) or ? (neutral). The modifiers redirect and exp change evaluation. Reading left to right, the first match wins.

SPF record syntax, mechanisms and qualifiers explained term by term: what ip4, a, mx, include, the + - ~ ? qualifiers, redirect and exp mean, per RFC 7208.

On this page
  1. 01The anatomy of an SPF record
  2. 02The mechanisms, term by term
  3. 03The four qualifiers: +, -, ~, ?
  4. 04Modifiers: redirect and exp
  5. 05How to read an SPF record: a worked example
  6. 06Why syntax mistakes break mail
  7. 07ip4 vs a vs mx: which mechanism to use
  8. 08Common misconceptions about SPF syntax
  9. 09How this shows up in AI Emaily

If you are staring at a line that starts with 'v=spf1' and trying to work out what each piece does, this is the reference for you. SPF record syntax is built from three kinds of terms — mechanisms, qualifiers and modifiers — and once you can read them in order, an SPF record stops looking like a wall of colons and slashes.

This guide walks through every mechanism (ip4, ip6, a, mx, include, exists, ptr and all), all four qualifiers, and the redirect and exp modifiers, exactly as RFC 7208 defines them. The goal is that you can read any record you are handed, and edit one without breaking your mail.

The anatomy of an SPF record#

An SPF record is a single DNS TXT record published on a domain. It always begins with the version tag 'v=spf1', which RFC 7208 requires to be exactly that — a record that starts with anything else is not treated as SPF at all.

After the version comes a list of terms. Each term is either a mechanism (a test against the IP that delivered the message), a modifier (which changes how the record is evaluated), or a mechanism with an optional qualifier in front of it.

A receiving server reads the terms left to right and stops at the first mechanism that matches. That first match, together with its qualifier, decides the result — pass, fail, softfail or neutral. This 'first match wins' rule is why order matters, and why the catch-all 'all' always belongs at the very end.

The mechanisms, term by term#

A mechanism is a test against the sending IP address. RFC 7208 defines eight of them. Some check the IP directly and cost nothing; others ask DNS a question, which is why they count toward the lookup limit covered further down. When 'a' or 'mx' is written without a domain, the record's own domain is used.

MechanismWhat it matchesDNS lookup?
ip4:<ip/range>The sending IP falls inside the given IPv4 address or CIDR rangeNo
ip6:<ip/range>The sending IP falls inside the given IPv6 address or rangeNo
a / a:<domain>The sending IP is one of the domain's A or AAAA addressesYes
mx / mx:<domain>The sending IP is one of the domain's MX hostsYes
include:<domain>Runs a full SPF check on another domain; only its pass counts as a matchYes
exists:<domain>An A record exists for a domain name built with macrosYes
ptr / ptr:<domain>The reverse DNS of the sending IP maps back to the domain (discouraged)Yes
allAlways matches; used as the final catch-allNo

Do not publish ptr

RFC 7208 section 5.5 states the ptr mechanism 'SHOULD NOT be published.' It is slow, loads DNS heavily, and is unreliable. Compliant receivers must still support it, but no modern record should include it — use ip4, ip6 or include instead.

The four qualifiers: +, -, ~, ?#

Every mechanism can carry a qualifier — one character in front of it that says what a match means. RFC 7208 section 4.6.2 defines four, and the qualifier is optional: if you leave it off, it defaults to '+'.

QualifierNameResult when the mechanism matchesCommon use
+PassThe sender is authorizedDefault — rarely written out
-FailThe sender is not authorized; rejectStrict policy: -all
~SoftFailProbably not authorized; accept but markCautious or testing policy: ~all
?NeutralNo statement either wayMonitoring only: ?all

Because the default is '+', 'include:_spf.google.com' and '+include:_spf.google.com' mean exactly the same thing. The qualifier you almost always write out is the one on the final 'all', because it sets the policy for every sender you did not list. '-all' hard-fails them; '~all' softens it to a warning; '?all' says nothing at all.

Modifiers: redirect and exp#

Modifiers are not mechanisms. RFC 7208 section 6 is blunt about it: 'Modifiers are not mechanisms. They do not return match or not-match.' They change how the record is evaluated rather than test an IP, and each may appear at most once, after the mechanisms.

'redirect=<domain>' hands the whole evaluation to another domain's SPF record. It only takes effect if none of the mechanisms above it matched — so you never combine 'redirect' with an 'all' mechanism, because 'all' matches everything first and 'redirect' would never fire. Use it to share one policy across several domains.

'exp=<domain>' points to a TXT record holding a human-readable explanation string, which a receiver may return when a message hard-fails. It changes the error text a sender sees, not the SPF result.

redirect is not include

'include' pulls another domain's authorized senders into your check and keeps evaluating your record afterward. 'redirect' replaces your record with another domain's entirely, and only if nothing above it matched. Mixing 'redirect' with 'all' is a common mistake — the 'all' wins first, so 'redirect' is ignored.

How to read an SPF record: a worked example#

Put the rules together on a real record. Take this one, which is a common shape for a domain sending through Microsoft 365 plus one on-premises server.

A receiving mail server walking an SPF record left to right, testing the sending IP against each mechanism until the first one matches, then using that mechanism's qualifier to decide pass, fail or softfail
Evaluation stops at the first mechanism that matches; the qualifier on that mechanism sets the result.
v=spf1 ip4:198.51.100.10 include:spf.protection.outlook.com ~all
v=spf1The version tag. Marks this TXT record as SPF. Required, exactly this.
ip4:198.51.100.10Pass if the message came from this exact IP. Costs no DNS lookup.
include:spf.protection.outlook.comRun a full SPF check on Microsoft's domain; a pass there authorizes the sender. One lookup, plus any it nests.
~allEverything else is a softfail — accepted but marked suspicious.

A receiver checks these terms in order. If the message came from 198.51.100.10, it passes on the first term and never looks further. If it came from Microsoft's servers, the 'include' passes. Anything else reaches '~all' and is marked. Reverse the order and put '~all' first, and every message would softfail — which is exactly why 'all' is always last.

Why syntax mistakes break mail#

Small syntax errors have real consequences. A record that starts 'v=spf2', uses 'include=' with an equals sign instead of a colon, or leaves a stray space after a colon is not parsed as SPF, so your legitimate mail quietly loses a layer of protection.

The most common breakage is the DNS lookup limit. RFC 7208 section 4.6.4 requires evaluators to limit the total number of DNS-querying terms to 10. The mechanisms that count are 'include', 'a', 'mx', 'ptr' and 'exists', plus the 'redirect' modifier. 'ip4', 'ip6' and 'all' cost nothing — a record can list hundreds of IPs without a single lookup.

Nested includes count toward the same ten, so a handful of provider includes can quietly push you over. Exceed the limit and the record returns 'permerror', which most receivers treat as a fail. The spec also recommends limiting 'void' lookups — queries that return no record — to two.

ip4 vs a vs mx: which mechanism to use#

These three all authorize your own servers, but they behave very differently. The choice is really stability versus DNS lookups.

MechanismAuthorizesDNS lookupsWhen to use
ip4 / ip6A fixed IP or CIDR range you type inNoneYou know the exact IPs and they rarely change
aWhatever A/AAAA record the domain resolves to nowOneThe sending host shares the domain's A record
mxWhatever hosts receive mail for the domain nowOne (up to 10 address records)Outbound mail leaves from the servers that receive it

'ip4' never costs a lookup but must be updated by hand when an address changes. 'a' and 'mx' track DNS automatically, so they follow an IP change on their own — but each spends one of your ten lookups, and 'mx' can resolve to several hosts, each of which the receiver must resolve, capped at 10 address records by the spec.

Common misconceptions about SPF syntax#

  • 'include' copies the other record's text. It does not — it runs a separate SPF check on that domain, and only a pass there counts as a match (RFC 7208 section 5.2).
  • You must write the '+' qualifier. You do not; a missing qualifier defaults to '+' (pass).
  • ip4 entries count toward the 10-lookup limit. They do not — only DNS-querying terms do.
  • 'ptr' is a normal mechanism to use. RFC 7208 says it SHOULD NOT be published; it is slow and unreliable.
  • 'redirect' and 'include' are interchangeable. They are not: 'include' adds senders and continues, 'redirect' replaces the record and only fires if nothing matched.
  • A domain can have several SPF records. Only one is valid — two or more return 'permerror'.

Syntax right, still spoofable

A perfectly valid SPF record does not stop spoofing on its own. SPF checks the hidden envelope domain, not the visible 'From' a reader sees. Only DMARC ties the authenticated domain to that visible address. Publish SPF, DKIM and DMARC together.

How this shows up in AI Emaily#

AI Emaily is a mail client, so it does not publish or edit your SPF record — that record lives in your domain's DNS, and you change it at your registrar, not inside an email app. To test a record's syntax or count its lookups, use a dedicated SPF checker; that is not a job we do.

Where SPF touches AI Emaily is on the reading side. It treats every incoming message as untrusted input, and authentication results — SPF among them — are part of how it decides which mail is genuine and which shows the hallmarks of spoofing or phishing. When AI Emaily sends on your behalf, it goes through your existing provider's servers, which your current 'include' already authorizes, so your syntax does not change. We build AI Emaily, and the job it does is triaging and defending the mail that reaches you — not managing your DNS.

Frequently asked

Nafiul Hasan

Written by

Nafiul Hasan

Nafiul Hasan is an entrepreneur and AI automation system builder with 10+ years of experience turning messy, manual workflows into reliable automated systems. He designs and ships AI enterprise solutions end-to-end — the agent logic, the data plumbing, and the product people actually use — and founded AI Emaily to give busy professionals their attention back. He writes here from the builder's seat: what works, what breaks, and how to put AI to work without giving up control.

EntrepreneurAI Automation System BuilderAI EnthusiastBuilds AI Enterprise Solutions10+ years experience
More from Nafiul
Ready when you are

Read your inbox with SPF on your side

AI Emaily does not manage your DNS, but it treats every message as untrusted and uses authentication signals to flag spoofing and phishing before you act. Start with a 7-day free trial on Pro — a card is required, and it is $0 if you cancel before day 7.

  • 7-day free trial
  • Cancel anytime
  • Every provider