Flattening answers "who may send" by copying a list. Dynamic SPF answers it by asking a question at the moment of the connection. It is the only approach that solves the lookup budget without creating something that goes stale.
The shape
v=spf1 exists:%{i}._spf.example.com -allOne term, one lookup, no list. For a connection from 198.51.100.7 the receiver resolves 198.51.100.7._spf.example.com. If it exists the mechanism matches; if it does not, evaluation moves on and hits -all.
The record never changes. Authorisation lives in the zone behind it, and changing who can send is a DNS operation rather than an SPF edit.
What the DNS side has to do
This is the part that decides whether you can have it. The zone must answer for every authorised address, and there are two ways to do that:
| Approach | How | Needs |
|---|---|---|
| Materialised | Write an actual record for every authorised address into the zone. | Automation to generate and prune it. Works on any DNS provider, and the zone can get very large. |
| Synthesised | Answer the query at request time from a backend that knows the current sender set. | A DNS provider that supports a dynamic backend. Most do not. |
The honest constraint
Dynamic SPF is the right architecture and it is not available to everyone, because it depends on something your DNS provider either offers or does not. If yours does not, the realistic options are delegation and removal — not flattening as a first resort.
What you give up
- Readability. The record no longer documents who can send. Anyone auditing you — including you, in a year — must expand and query.
- Cacheability. Every distinct address is a distinct name, so receivers get far fewer cache hits than they would on a handful of includes.
- A new dependency. If the backend is down, the lookup is a void one, and enough of those are a permerror. A stale flattened record at least keeps working for the hosts that did not move.
Verifying one
Expand by hand and query. If the name resolves, the mechanism matches — the content is irrelevant, existence is the answer:
# authorised host dig +short A 198.51.100.7._spf.example.com -> any answer = pass # unauthorised host dig +short A 203.0.113.9._spf.example.com -> NXDOMAIN = no match, falls through to -all
Test both directions when you deploy one. A backend that answers for everything is an +all with extra steps, and it will not look wrong in the record.