An SPF record is a sequence of tests with explicit outcomes. The difference between a colon, an equals sign and a qualifier matters. A syntactically valid change can also alter who is authorised, which DNS dependencies are reached, or where processing stops.
Use RFC 7208 §4.6, RFC 7208 §5.2 and RFC 7208 §6 as the grammar and evaluation reference. Treat the examples below as synthetic fixtures, not records to paste into production.
Separate mechanisms from modifiers
A mechanism asks whether an input matches: examples include ip4, ip6, a, mx, include, exists and all. A qualifier attached to a matching mechanism determines its result: + gives pass, - fail, ~ softfail and ? neutral. Under RFC 7208 §4.6.2, if the qualifier is omitted, + is implied.
Modifiers use an equals sign. redirect=policy.example.com delegates evaluation when no mechanism matches. exp=explain.example.com identifies explanatory text for certain fail results. They are not additional matching mechanisms. Recognised modifiers have their own rules; syntactically valid unrecognised modifiers are ignored, while an unrecognised mechanism is an error.
A record begins with v=spf1 and is published as a DNS TXT record at the evaluated identity. Two separate SPF records at the same owner are not a way to combine senders; SPF record selection returns permerror under RFC 7208 §4.5. Multiple quoted strings inside one TXT record are a different representation and are concatenated without inserted spaces under RFC 7208 §3.3.
Trace a complete example
Assume valid syntax, one SPF record at each name and successful DNS retrieval. Suppose the policy is v=spf1 ip4:192.0.2.20 include:service.example.net -all. The included policy is v=spf1 ip4:198.51.100.10 -all.
For 192.0.2.20, the first mechanism matches and returns pass. The include is not needed for that evaluation path. For 198.51.100.10, the first mechanism does not match, the included policy returns pass, and the outer include matches with its default + qualifier. For 203.0.113.7, the included policy returns fail; that makes include not match, and the outer -all supplies the final fail.
This trace explains why include is not text substitution. The included -all does not immediately reject all addresses that missed the service's policy. The parent policy resumes after a nonmatching include result.
Understand where processing stops
A matching mechanism ends ordinary mechanism evaluation. An error from a reached term can also end evaluation. Rearranging terms can therefore change the result or the dependencies a receiver must query.
The all mechanism always matches, so terms after it cannot supply additional authorisation. A redirect modifier is ignored when an all mechanism is present anywhere in the record. Writing -all redirect=policy.example.com does not create an emergency fallback.
Under RFC 7208 §4.7, if no mechanism matches and there is no redirect, the default result is neutral. Make the intended ending explicit so that another administrator does not have to infer whether the omission was intentional.
Check the path and the entire record
Before publishing, validate the complete syntax, not only the portion expected to match a familiar IP. An invalid policy cannot be made safe by assuming a favourable early match. Then evaluate authorised IPv4 and IPv6 inputs, an unauthorised input, and dependencies that fail or return no SPF record.
The main DNS term limit covers evaluated include, a, mx, ptr, exists and redirect terms, including recursion. It is not a count of quoted TXT fragments. Keep a trace showing both the selected outcome and the DNS terms reached. Budget for provider dependency changes instead of designing a policy with no headroom.
Make a reviewable change
Write the proposed record next to the current record and explain the intended permission difference in ordinary language. Preserve the prior value and TTL, check the result at authoritative DNS and through a recursive resolver, then test representative received messages.
The acceptance condition is not simply that a parser accepts the string. The intended senders should pass for the correct identity, unintended senders should not gain permission, and required DMARC alignment should remain intact.
The practical takeaway
Read each SPF term by type, qualifier and position. A clear evaluation trace makes syntax review a permission review rather than a punctuation exercise.
Keep the decision reproducible
In the two-record fixture, the local address passes before include, the service address passes via child pass, and the unrelated address reaches the parent’s final fail. If the child instead has no SPF record, the reached include returns permerror. Keep that error case separate from child fail/nonmatch. These are analytical outcomes, not an executed parser or verifier test.