An SPF record with a syntax error returns permerror, so receivers do not evaluate it at all. RFC 7208 §4.6 says “if there are any syntax errors anywhere in the record, check_host() returns immediately with the result ‘permerror’.” Paste your domain into the SPF checker, find the red line in the table below, and apply the fix. One typo anywhere in the record is enough to disable all of it.
Messages and fixes
The first column is the text the checker shows.
| Checker message | Cause | Fix |
|---|---|---|
| Record does not start with v=spf1 (RFC 7208 §4.5) | Text before v=spf1, v=spf 1, spf1, a stray quote | Make v=spf1 the first characters of the value |
| Unparseable term ”…” | A term that matches no mechanism or modifier shape, e.g. the 192.0.2.1 left over from ip4 192.0.2.1 (space instead of colon, which also triggers the “ip4 requires an address” line) | Use ip4:address and include:domain with a colon |
| Unknown mechanism ”…” → permerror (§4.6.1) | Typo such as inlcude:, ipv4:, mxx | Correct the name: include, a, mx, ptr, ip4, ip6, exists, all |
| “include” requires a domain (§5.2) | include: with nothing after it, or include alone | Add the provider’s domain |
| “exists” requires a domain (§5.2) | Same for exists: | Add a domain |
| “ip4” / “ip6” requires an address | ip4: empty | Add an address or range |
| “+all” allows any sender — effectively no SPF protection | +all or all with the default + | Use ~all or -all |
| Terms after “all” are never evaluated (§5.1) | Mechanism written after all | Move all to the end |
| …: included domain has no SPF record → permerror for that include (§5.2) | include: of a domain that publishes no v=spf1 record | Correct the domain name or remove the include |
| N SPF records on … → permerror (§3.2): must be exactly one | More than one v=spf1 TXT | See Two SPF records |
| N DNS lookups — limit is 10 (§4.6.4) → permerror at many receivers | Too many include, a, mx terms | See SPF too many lookups |
| N void lookups — limit is 2 (§4.6.4) | Includes or names that return nothing | Remove dead names |
| Unknown modifier ”…” is ignored by receivers (§6) | Unrecognized name=value term | Warning only; delete if it is a typo for redirect= or exp= |
| ptr is deprecated and slow (§5.5) | ptr mechanism | Replace with ip4/ip6 or an include |
The section numbers in the left column are the checker’s own. Where RFC 7208 is more general than the message, for example for unknown mechanisms, the rule behind it is the syntax-error rule in §4.6. Warnings (unknown modifier, ptr, terms after all) describe terms that are ignored or discouraged, not permerrors.
Examples
Typos and their corrections:
Wrong: v=spf1 inlcude:_spf.google.com ~all
Right: v=spf1 include:_spf.google.com ~all
Wrong: v=spf1 ip4:192.0.2.0/33 ~all
Right: v=spf1 ip4:192.0.2.0/24 ~all
Wrong: v=spf1 include: ~all
Right: v=spf1 include:spf.protection.outlook.com ~all
IP and CIDR errors
An ip4: value takes a full dotted address, optionally followed by / and a prefix length from 0 to 32. ip6: accepts a prefix length from 0 to 128 (RFC 7208 §5.6). The RFC also says: “It is not permitted to omit parts of the IP address instead of using CIDR notations”, so ip4:192.0.2 is invalid. Values outside those ranges do not match the grammar (§12), so they fall under the syntax-error rule in §4.6 and the result is permerror. The checker reports each case separately:
| Mistake | Checker message |
|---|---|
| Prefix too long | Prefix length "/33" in ip4:192.0.2.0/33 is out of range: ip4 allows 0 to 32 → permerror (§5.6, §12) |
| Leading zeros | Prefix length "/024" in ip4:192.0.2.0/024 has leading zeros; write it as /24 → permerror (§12) |
| Non-numeric prefix | Invalid prefix length "/x" in ip4:1.2.3.4/x: must be a number from 0 to 32 → permerror (§5.6, §12) |
| Incomplete address | Invalid IPv4 address "192.0.2" in ip4:192.0.2 → permerror (§5.6, §12) |
The ip6: messages are the same with the 0 to 128 range. A frequent real-world mistake is pasting a network with a host bit set, such as 192.0.2.5/24; that parses, but it authorizes the whole /24, which may be more than intended.
Include of a domain without SPF
When include: points at a domain with no SPF record, the nested evaluation returns “none”, and §5.2 maps that to permerror. It happens when you misspell a provider’s domain or when the provider changed its hostname. Compare the include with the provider’s own documentation. The provider setup check shows the documented include for a provider and whether your domain uses it. For setup from the beginning, see How to set up an SPF record.
The +all trap
v=spf1 +all, or the shorter v=spf1 all, is syntactically valid and passes every sender. That is why the checker lists it as an error although receivers do not return permerror. It usually comes from a test record that was never replaced. Use ~all or -all; see ~all vs -all.
FAQ
Does one typo break the whole record? Yes. A syntax error anywhere returns permerror for the full evaluation (RFC 7208 §4.6).
Is an unknown modifier also a permerror? No. Section 6 says unrecognized modifiers “MUST be ignored”, so the checker only warns. An unknown mechanism is a syntax error.
My record looks right but the checker still reports an error. Read the line beginning with the included domain. Errors inside an included record are shown with that domain’s name, and you cannot edit them; contact the provider or remove the include.
Sources
- RFC 7208, §4.6 Evaluating SPF Records (syntax errors): https://www.rfc-editor.org/rfc/rfc7208#section-4.6
- RFC 7208, §5.2 “include”: https://www.rfc-editor.org/rfc/rfc7208#section-5.2
- RFC 7208, §5.6 “ip4” and “ip6”: https://www.rfc-editor.org/rfc/rfc7208#section-5.6
- RFC 7208, §6 Modifier Definitions: https://www.rfc-editor.org/rfc/rfc7208#section-6
- RFC 7208, §4.5 Selecting Records: https://www.rfc-editor.org/rfc/rfc7208#section-4.5