%{ir}%i198.51.100.4242.100.51.198Expand SPF macros — %{i}, %{s}, %{d}, %{l}, %{o}, %{h}, %{v}, %{p} — with your own evaluation context. See exactly what each macro expands to before publishing the record, and understand the per-IP and per-sender authorisation patterns most receivers never document.
exists:42.100.51.198.in-addr._spf.example.com%{ir}%i198.51.100.4242.100.51.198%{v}%vin-addrin-addr%{d}%dexample.comexample.comRFC 7208 §7 defines a tiny templating language embedded inside SPF mechanism values. At evaluation time, receivers substitute placeholders like %{i} (the client IP) and %{s} (the sender address) with values from the current message. The result lets you put per-sender or per-IP logic into a single static SPF record — a trick most ESPs use and almost no admin debugs comfortably because there's never a clean way to see the expansion.
The debugger above lets you paste any mechanism with macros, set the evaluation context (client IP, sender, HELO, domain), and see the final expanded form plus a macro-by-macro breakdown showing the base value, the transform, and the output.
| Macro | Meaning | Common use |
|---|---|---|
%{s} | Sender — full envelope MAIL FROM address | Per-sender allowlisting |
%{l} | Local-part of the sender | Mailbox-specific routing (e.g. only alerts@ via Pagerduty) |
%{o} | Domain of the sender | Per-tenant SPF includes in shared infra |
%{d} | Current evaluation domain | Build self-referential records |
%{i} | Client IP (IPv6 → 32 dot-separated nibbles) | Per-IP allowlisting via DNS zone |
%{p} | Validated PTR (discouraged) | Almost always returns "unknown" — avoid |
%{v} | Literal in-addr for IPv4 or ip6 for IPv6 | Build reverse-DNS-style zones |
%{h} | HELO/EHLO domain from the SMTP session | Detect mismatch between HELO and MAIL FROM domain |
%{c} | Client IP (textual form). Only valid in exp=. | Explanation strings |
%{r} | Receiving server domain. Only valid in exp=. | Explanation strings |
%{t} | Current Unix timestamp. Only valid in exp=. | Time-bound explanation strings |
Every macro accepts optional transformations between the letter and the closing brace: %{letter[digits][r][delimiter]}.
%{i2} on 192.0.2.10 yields 2.10 (last 2 dot-separated parts).%{ir} on 192.0.2.10 yields 10.2.0.192.. - + , / _ =. Splits the base value on any of the listed characters before applying digits/reverse. Example: %{l-} on foo-bar-baz splits on - giving three parts.The SPF Flattener can only resolve include chains to static IPs at the time of evaluation. Macros are evaluated per-message using runtime context (the client IP, the sender). A record containing exists:%{ir}.%{v}._spf.%{d} cannot be pre-flattened — there is no single static answer. The Flattener flags this with a macro_in_mechanism warning and skips the expansion.
%{p} is the validated PTR domain — the receiver runs a reverse-DNS lookup on the client IP and confirms the forward A record matches. Per RFC 7208 §7.2, if validation fails (the typical case for IPs without proper FCrDNS), %{p} expands to unknown. Many receivers skip the PTR lookup entirely for performance. Combined: %{p} almost always returns unknown — design around it, not for it.
The presets above the tool cover the most-cited patterns. Two live examples worth knowing:
exists:%{ir}.%{v}._spf.%{d} looks up 10.2.0.192.in-addr._spf.example.com for an IPv4 client. Publish A records under that zone to authorise individual IPs without bloating the static SPF record.exists:%{l}.%{o}._spf.%{d} looks up alice.user.example.com._spf.example.com. Lets you restrict particular mailboxes to particular send paths — useful when alerts@ should only ever originate from your monitoring infra.Read the complete SPF guide to learn more.
Continuous monitoring, aggregate report parsing, and actionable insights for all your email authentication protocols.
Start Free