DMARC Engine
Home/Blog/SPF macros explained
Blog

SPF macros explained

SPF macros turn a record into a small evaluated language, substituting live transaction data like the connecting IP into DNS queries. Here is the %{...} syntax, the macro letters and modifiers, the real use cases that justify them, and the lookup-count and security trade-offs you take on when you publish one.

22 June 2026 · 5 min read

SPF macros explained

SPF macros are placeholders such as %{i} or %{d} that a receiver replaces with values from the current message before it runs a DNS lookup. They let a single SPF term ask a different question for every connecting IP address or sender, usually through the exists: mechanism. Most domains never need to write one, but large senders use them, so it helps to be able to read them. Macros are defined in section 7 of RFC 7208.

A macro in one example

Example
exists:%{i}._spf.example.com

Before querying DNS, the receiver replaces %{i} with the IP address of the server that is connecting. If 192.0.2.3 connects, the receiver looks up:

Example
192.0.2.3._spf.example.com

If that name has an A record, exists: matches and SPF passes. If it does not exist, evaluation moves on to the next term. The decision has moved out of the SPF string and into your DNS zone, where you can publish one record per authorised IP.

Salesforce's SPF record uses exactly this pattern at the time of writing: exists:%{i}._spf.mta.salesforce.com.

The macro letters

MacroExpands to
%{s}The envelope sender (MAIL FROM), as local-part@domain
%{l}The local-part of the envelope sender
%{o}The domain of the envelope sender
%{d}The domain currently being checked (changes inside include: and redirect=)
%{i}The connecting IP address; IPv6 is written as dot-separated nibbles
%{p}The validated reverse DNS name of the IP. RFC 7208 says it SHOULD NOT be published
%{v}in-addr for IPv4, ip6 for IPv6
%{h}The HELO or EHLO name the client gave

Three more letters, %{c}, %{r} and %{t}, are allowed only in the explanation text used by the exp= modifier, not in terms that decide the result.

Transformers: reverse, trim and split

Values often arrive in the wrong shape for a DNS name, so a macro can transform them inside the braces:

  • A number keeps that many parts from the right. %{d2} on mail.corp.example.com gives example.com.
  • r reverses the parts. %{ir} on 192.0.2.3 gives 3.2.0.192.
  • Delimiters change what the value is split on. The default is ., and RFC 7208 also allows -, +, ,, /, _ and =. For example, %{l-} splits the local-part on hyphens.

RFC 7208's own worked example: for a sender at email.example.com connecting from 192.0.2.3,

Example
%{ir}.%{v}._spf.%{d2}

expands to 3.2.0.192.in-addr._spf.example.com. A similar pattern appears in Proofpoint's hosted SPF record at the time of writing: include:%{ir}.%{v}.%{d}.spf.has.pphosted.com.

Case and escapes

  • An uppercase macro letter expands the same way and is then URL-escaped. %{S} gives a percent-encoded sender. In exists: terms you almost always want lowercase.
  • A literal percent sign is written %%. %_ gives a space and %- gives a URL-encoded space (%20).

A stray % that is not part of one of these forms makes the record invalid.

Macros still count towards the ten-lookup limit

SPF allows at most ten mechanisms and modifiers that cause DNS lookups (include, a, mx, ptr, exists and redirect), and more than ten returns a permanent error. An exists: term costs one lookup whether or not it contains a macro. Used well, a single exists: term can replace several include: terms, each of which may hide nested lookups, so macros can help you stay under the limit. They do not get you out of it.

Macros also make the void lookup limit easier to hit. RFC 7208 says implementations SHOULD limit lookups that return no answer to two, and that exceeding the limit produces a permanent error. An exists: design returns no answer for every IP that is not authorised, by design. If other terms in the same record also return nothing, a correct-looking record can fail. Fixing too many SPF DNS lookups covers the counting.

Security and privacy

RFC 7208 is direct about the risks:

  • Senders control part of your DNS queries. Section 11.5.3 notes that macros let senders inject arbitrary text into the receiver's DNS queries. The envelope sender, and therefore %{s}, %{l} and %{o}, is chosen by whoever connects, including an attacker. The connecting IP, %{i}, cannot be forged in the same way, which makes it the safer basis for a decision.
  • Queries reveal who is sending. Section 11.6 warns that queries caused by exists: can contain information about who is sending mail and where it is going, and those queries reach the domain owner's DNS servers.
  • %{p} depends on someone else's reverse DNS. The connecting IP's owner controls its PTR record, the lookup is slow, and RFC 7208 says the macro should not be published.
  • Caching suffers. RFC 7208 advises sending domains to avoid %{s}, %{l}, %{o} and %{h} because they severely limit the ability to cache results.

A macro record is also harder to audit. A plain list of ip4: and include: terms can be checked by reading it; a macro record can only be checked by working out what it expands to for real connections.

Should you use macros?

For almost every domain, no. If you send through a known set of providers, a short literal record of include:, ip4: and ip6: terms ending in ~all or -all is easier to read, cannot leak information and stays within the limits if you keep the include count down. The SPF generator produces that kind of record.

Macros make sense when you authorise a large and changing set of IP addresses, base the decision on %{i}, and test what the record expands to on an ongoing basis. Otherwise, their main relevance is that your providers' records may use them, and you can now read what those records do.

Check what your own record evaluates to with the SPF checker, and read RFC 7208 explained for the rest of the SPF standard. If you are over the lookup limit, hosted SPF flattens your record automatically and keeps it under ten as your senders change, without macros.

Share

See where your domain stands today

Run a free DMARC scan, then let us take you to enforced p=reject with no email outage.