CertaDNS
Skip to lesson

Macros · lesson 1 of 2

Macro syntax

After this lesson you can

Expand any SPF macro by hand for a given connection.

Assumes you have read The algorithm, term by term.

Macros are the part of SPF that almost no course covers, and they are how the largest senders on the internet stay inside the lookup budget. Once you can expand one by hand, records that looked like line noise become readable.

The shape

%{ir}
  1. % opens a macro.
  2. {} delimit it.
  3. i is the macro letter — which value to substitute.
  4. r is a transformer — here, reverse the parts.

The letters worth knowing

LetterExpands toFor 1.2.3.4 sending as user@acme.com
%{i}The connecting IP.1.2.3.4
%{d}The domain being evaluated.acme.com
%{s}The whole sender address.user@acme.com
%{l}The local part.user
%{o}The sender’s domain.acme.com
%{h}The HELO name.mail.acme.com
%{v}The literal "in-addr" or "ip6".in-addr

RFC 7208 §7.1 has the full set. These seven cover everything you will meet in the wild.

Transformers

  • r — reverse. %{ir} turns 1.2.3.4 into 4.3.2.1, which is the order reverse-DNS names use.
  • A digit — keep the rightmost N parts. %{d2} on mail.eu.acme.com gives acme.com.
  • Both together, digit after r. %{ir4} is reverse, then keep four.
  • Uppercase means URL-escape. %{S} is the sender percent-encoded — for macros that end up in an exp URL.

Three literals you will see

%% is a literal percent sign, %_ a space, and %- an escaped space (%20). They exist because a macro string may end up inside a URL.

Expanding one by hand

Take the record booking.com published on 2026-09-15:

$ dig +short TXT booking.com | grep spf1
v=spf1 include:%{ir}.%{v}.%{d}.spf.has.pphosted.com -all

For a connection from 1.2.3.4 evaluating booking.com:

%{ir} -> 4.3.2.1        (i reversed)
%{v}  -> in-addr        (IPv4)
%{d}  -> booking.com

include:4.3.2.1.in-addr.booking.com.spf.has.pphosted.com

And that name really resolves — which is how you check your expansion is right rather than merely plausible:

$ dig +short TXT 4.3.2.1.in-addr.booking.com.spf.has.pphosted.com
"v=spf1 exists:%{i}._spf.sparkpostmail.com exists:%{ir}.spf.mail.service-now.com -all"
Checked 2026-09-15. The expansion resolves to a second record that uses two more macros — a live, two-level chain.

One include in the published record, and behind it a per-connection answer assembled from the connecting address itself. The next lesson is about why anyone would build that.

Try it on a real domain

Free, no account, public DNS only.

Last reviewed