The SPF redirect trap: why -all can make redirect= useless
SPF records often look simple until you start combining mechanisms and modifiers.
One particularly easy mistake is to write a record like this:
v=spf1 include:_spf.google.com -all redirect=_spf.example.com
At first glance, it seems reasonable:
- authorize Google,
- reject everything else,
- and use another SPF policy through
redirect=.
But the redirect= part will never be used.
The reason is an important detail of how SPF evaluation works.
redirect= is not a fallback after -all
An SPF record is evaluated mechanism by mechanism.
For example:
v=spf1 ip4:192.0.2.10 include:_spf.google.com -all
The receiver checks the mechanisms until one matches.
The all mechanism is special because it always matches.
That means:
-all
effectively says:
If nothing before this matched, return SPF Fail.
Now consider this record again:
v=spf1 include:_spf.google.com -all redirect=_spf.example.com
Once SPF reaches -all, it already has a result.
There is no reason to evaluate redirect=.
The redirect modifier is only used when none of the mechanisms in the record produce a match.
Because all always matches, a record containing all prevents redirect= from being used.
What redirect= is actually for
The redirect modifier is useful when several domains should share one central SPF policy.
Imagine these domains:
example.com
example.net
example.org
Instead of maintaining the same SPF configuration independently on every domain, they can redirect to a central policy.
For example:
example.com TXT "v=spf1 redirect=_spf.example.com"
example.net TXT "v=spf1 redirect=_spf.example.com"
example.org TXT "v=spf1 redirect=_spf.example.com"
And the central record might contain:
_spf.example.com TXT "v=spf1 ip4:192.0.2.10 include:_spf.google.com -all"
Now the sending policy can be maintained in one place.
This is very different from include:.
redirect= vs include:
These two are easy to confuse.
include:
Use include: when you want to authorize senders defined by another SPF record as part of your own policy.
Example:
v=spf1 include:_spf.google.com ip4:192.0.2.10 -all
This says:
Google is allowed, this IP is allowed, and everything else should fail.
Your domain still owns the final policy.
redirect=
Use redirect= when another domain should provide the entire remaining SPF policy.
Example:
v=spf1 redirect=_spf.example.com
A useful mental shortcut is:
include: → add another policy
redirect= → use another policy
That distinction matters when debugging SPF records.
A common broken configuration
Consider this:
v=spf1 ip4:192.0.2.10 -all redirect=_spf.provider.com
Someone may expect the following behavior:
- allow
192.0.2.10, - if it does not match, check
_spf.provider.com, - otherwise fail.
But SPF does not work that way.
The real evaluation is:
- check
ip4:192.0.2.10, - if it does not match, evaluate
-all, -
allmatches every sender, - return SPF Fail,
-
redirect=is never used.
If you really want the provider to define the policy, the record should instead look like:
v=spf1 redirect=_spf.provider.com
If you want both your own IP address and the provider's authorized senders, include: is usually the appropriate mechanism:
v=spf1 ip4:192.0.2.10 include:_spf.provider.com -all
The position of redirect= can be misleading
Another subtle point is that redirect= is a modifier, not a mechanism.
So it should not be read as if SPF simply processes it from left to right like ip4, include, mx, or all.
This record:
v=spf1 redirect=_spf.example.com -all
does not mean:
Try redirect first, then
-all.
The presence of the all mechanism still means SPF can produce a result without using the redirect.
A good mental model is:
Evaluate the SPF mechanisms first.
Only if none of them match canredirect=provide another SPF record to evaluate.
Why this matters in production
A broken SPF redirect can be surprisingly difficult to notice.
The DNS record is syntactically valid.
A DNS lookup succeeds.
The domain has an SPF record.
Yet mail from an expected sender may still fail SPF because the redirected policy was never evaluated.
This becomes especially confusing when organizations centralize SPF records across many domains.
A small mistake in the parent policy can affect multiple domains at once.
When debugging this kind of issue, check:
- whether the record contains an
allmechanism, - whether
redirect=is actually reachable, - whether you really need
redirect=or should useinclude:, - the SPF result returned by the redirected domain,
- the total number of DNS-querying SPF terms.
A simple rule to remember
If your SPF record contains:
-all
and also:
redirect=...
you should take a closer look.
In most cases, that combination indicates that the redirect is not doing what the author intended.
Use:
redirect=
when another domain should define the SPF policy.
Use:
include:
when another SPF policy should be incorporated into your own.
And use:
-all
when you are ready to explicitly fail senders that did not match any previous mechanism.
If you are debugging a more complex configuration, I wrote a more detailed guide to the SPF redirect modifier, including redirect= vs include:, DNS lookup behavior, common errors, and practical examples.
You can also inspect an SPF record directly with the MXFend SPF checker.
This article was originally published by DEV Community and written by Petr Michal.
Read original article on DEV Community