SPF Mechanisms Deep Dive: How include:, mx, and Macros Each Consume Your 10-Lookup Budget
A single `include:` mechanism can secretly consume several of SPF's 10 allowed DNS lookups, depending on what the included record itself contains. Here's exactly how each SPF mechanism (ip4, a, mx, include, exists, ptr, redirect) consumes lookups, how macro expansion works for dynamic IP-based checks, and how mechanism ordering and qualifiers interact.
By sadiqbd Β· June 13, 2026
An SPF record with a single include: mechanism can secretly expand into 10+ DNS lookups β and SPF has a hard limit of exactly 10, after which the entire record fails closed
The previous SPF article on this site covered the 10-lookup limit and PermError at a high level. This article goes deeper into the individual mechanisms that make up an SPF record β what each one actually checks, which ones consume lookups from that limit, and how macro expansion (a lesser-known SPF feature) works for the specific cases where it's used.
SPF mechanisms and their lookup cost
An SPF record consists of a sequence of mechanisms, evaluated left to right, each either matching (causing SPF evaluation to stop with that mechanism's qualifier result β pass, fail, softfail, neutral) or not matching (proceeding to the next mechanism):
ip4: and ip6: β specify an IPv4 or IPv6 address or CIDR range directly. Zero additional DNS lookups β the IP comparison is done directly against the value in the record itself.
v=spf1 ip4:203.0.113.0/24 ip4:198.51.100.50 -all
a β matches if the connecting IP matches any A/AAAA record for the domain (or a specified domain, via a:domain.com). Counts as one DNS lookup (to retrieve the A/AAAA records for the relevant domain).
mx β matches if the connecting IP matches any of the IP addresses of the domain's MX records. This involves first looking up the MX records (one lookup), then for each MX hostname, looking up its A/AAAA records (additional lookups) β meaning a single mx mechanism can consume multiple lookups from the 10-lookup budget, depending on how many MX records the domain has.
include: β the most common source of lookup-limit issues. This mechanism says "evaluate the SPF record of this other domain, as if it were inserted here." The include: itself counts as one lookup β but if the included domain's SPF record itself contains further include:, a, mx, or other lookup-consuming mechanisms, those count too, cumulatively, against the original domain's 10-lookup limit. This is why a single include: for, say, a marketing platform's SPF record, can β depending on how that platform's SPF record is constructed β consume several lookups from your budget, not just the one for the include: itself.
exists: β performs a DNS lookup for a constructed domain name (often used with macros, discussed below) and matches if that lookup returns any result (regardless of what the result actually is β existence alone is the signal). Counts as one lookup.
ptr β (largely deprecated/discouraged in modern SPF guidance) performs a reverse DNS lookup on the connecting IP, then checks whether the resulting hostname is within the specified domain. Counts as one lookup for the PTR lookup itself, plus potentially additional lookups for the forward A/AAAA lookups needed to verify the PTR result β and is specifically called out in the SPF specification as something to avoid due to both the lookup cost and reliability concerns (given the IPv6 reverse DNS coverage issues discussed in a previous article, ptr mechanisms may simply fail to match for many IPv6 senders due to absent PTR records, independent of whether the sender is legitimate).
redirect= β different from include:: rather than "evaluate this other domain's record in addition to continuing with the rest of this record," redirect= says "if evaluation reaches this point without a prior match, entirely replace evaluation with the specified domain's SPF record" β it's effectively a way to say "this domain's SPF policy is identical to that other domain's policy," often used when an organisation wants multiple domains to share a single, centrally-maintained SPF policy. redirect= itself consumes a lookup, and (similar to include:) the redirected-to record's own lookups count cumulatively.
Macro expansion: SPF's lesser-known dynamic feature
SPF macros allow constructing dynamic hostnames for exists: (and some other contexts) based on properties of the current SPF check β most commonly the sender's IP address or the domain being checked.
Common macro letters:
%{i}β the sending IP address (in a format suitable for inclusion in a hostname β e.g., for IPv4, the dotted-decimal representation)%{s}β the envelope sender (MAIL FROM) email address%{d}β the domain being checked (the current domain in the SPF evaluation)%{h}β the HELO/EHLO domain presented by the connecting client
Example use case β IP-based allowlisting via DNS:
v=spf1 exists:%{i}.spf.example.com -all
This constructs a hostname using the sender's IP address (e.g., for IP 203.0.113.42, the constructed name might be something like 42.113.0.203.spf.example.com, depending on the exact macro formatting rules for IPv4) β and checks whether that specific hostname has any DNS record. This effectively allows an organisation to maintain a dynamic "list of authorised IPs" as DNS records (one record per authorised IP, at a hostname derived from that IP), checked via the exists: + macro combination β rather than listing IPs directly in the SPF record (which would consume record space and doesn't scale well for large/dynamic IP lists).
Why this is uncommon in practice: macro-based SPF is a more advanced/niche usage β most domains' SPF records don't use macros at all, relying instead on straightforward ip4:/ip6:/include: mechanisms. Macros are more likely to be encountered when included via a third-party service's SPF record (some email service providers may use macro-based techniques internally as part of their own SPF infrastructure) β a domain owner might never directly write macro syntax themselves, but could have macro-based mechanisms enter their effective SPF evaluation via an include: of such a provider's record.
All mechanisms support qualifiers β not just allow/deny
Each mechanism can be prefixed with a qualifier β + (Pass, the default if no qualifier is specified), - (Fail), ~ (SoftFail), or ? (Neutral):
v=spf1 ip4:203.0.113.0/24 -ip4:203.0.113.99 ~all
This example: IPs in the 203.0.113.0/24 range pass β except 203.0.113.99 specifically, which explicitly fails (perhaps a known-compromised host within an otherwise-trusted range that's been specifically excluded) β and anything else gets SoftFail (~all).
The order matters: SPF evaluation proceeds left to right and stops at the first matching mechanism β so a more specific exclusion (like the -ip4:203.0.113.99 above) needs to appear before any broader mechanism that would otherwise match that same IP first (if ~all or a broader ip4: range came first and matched, evaluation would stop there, never reaching the more specific exclusion later in the record).
Why understanding individual mechanisms helps with lookup-limit troubleshooting
When facing a PermError due to exceeding the 10-lookup limit (covered in the previous article's high-level treatment), understanding which mechanisms consume lookups, and how many, allows for targeted remediation:
- Flattening
include:mechanisms β if a third-party service's included SPF record resolves to a stable, relatively small set of IP ranges, replacing theinclude:with directip4:/ip6:mechanisms for those ranges eliminates the lookups associated with thatinclude:chain β at the cost of needing to manually update your SPF record if/when that third party changes their IP ranges (a maintenance trade-off, sometimes managed via automated tooling that periodically re-flattens and updates SPF records) - Removing unnecessary
mxoramechanisms β if these mechanisms were included historically but the domain's actual sending sources don't actually rely on matching against the domain's own A/MX records (e.g., all legitimate sending is via specific third-party services already covered byinclude:mechanisms), removing redundanta/mxmechanisms reduces lookup count without changing the effective policy (since those mechanisms weren't contributing any actual matches) - Avoiding
ptrβ as noted, generally recommended against both for lookup cost and reliability reasons
How to use the SPF Lookup tool on sadiqbd.com
- View the full expanded mechanism list, including resolving
include:chains, to see the cumulative lookup count and identify which specific mechanisms contribute most to the total - Identify mechanisms that could be flattened (
include:resolving to a small, stable IP set) vs those that genuinely need to remain dynamic (include:of a provider whose IP ranges change frequently, where flattening would create a maintenance burden) - Check mechanism ordering β particularly if specific exclusions (
-ip4:for a specific address within a broader allowed range) are present, verify they appear before any broader mechanism that would otherwise match first
Frequently Asked Questions
Does the 10-lookup limit apply to ip4:/ip6: mechanisms even if there are many of them?
No β as noted, ip4: and ip6: mechanisms (including many of them, each specifying different ranges) don't consume DNS lookups at all, since the IP comparison is direct against values already present in the record text. A record with twenty ip4: mechanisms and zero include:/a/mx/etc. mechanisms would have a lookup count of zero, regardless of how many ip4: entries it contains (though there's a separate, different limit on overall SPF record length/DNS TXT record size considerations, which is a distinct constraint from the lookup-count limit).
If redirect= replaces evaluation entirely, can a record have both redirect= and other mechanisms like -all?
The SPF specification indicates redirect= is generally used without a trailing all mechanism in the same record β since redirect=, when reached, takes over evaluation entirely (using the redirected domain's record, including that record's own terminal all mechanism) β including both redirect= and -all in the same record creates a degree of redundancy/ambiguity about which is meant to apply, and is generally avoided in well-constructed SPF records in favour of choosing one approach (either redirect= to delegate entirely, or include: plus your own terminal all mechanism, depending on the intended relationship between the domains).
Is the SPF Lookup tool free? Yes β completely free, no sign-up required.
Try the SPF Lookup tool free at sadiqbd.com β view your domain's full SPF record, expand include chains, and count DNS lookups.