Count the mechanisms that can query DNS

RFC 7208 limits terms that cause DNS queries during one SPF evaluation to ten. The mechanisms include, a, mx, ptr, and exists, plus the redirect modifier, belong in that accounting. Dependencies reached through an include can consume more of the same budget. Literal ip4, ip6, and all mechanisms do not require those SPF DNS lookups.

Do not count the policy’s initial TXT fetch as one of these terms, and do not assume ten visible include words are the only risk. An mx mechanism can trigger address lookups subject to its own limit. The discouraged ptr mechanism is expensive and should be reviewed rather than treated as a convenient shortcut.

Draw the dependency tree

Start at the exact MailFrom domain and list terms in evaluation order:

sender.example
├─ include:_spf.service-a.example
│  ├─ ip4:192.0.2.0/24
│  └─ include:_spf.shared.example
├─ a:mail.sender.example
└─ -all

The names and network are reserved examples. Fetch each referenced policy with bounded recursion and detect cycles. Record missing answers and DNS errors separately. RFC 7208 also recommends limiting “void lookups”—queries that return a valid negative result—to two; an unavailable lookup must not be silently counted as a clean non-match.

Follow representative paths

SPF stops at the first matching mechanism, so the path taken can differ by connecting IP. Test representative legitimate IPs and at least one nonmatching address in a local evaluator. Record which terms were reached and the final result. A static maximum expansion is useful risk evidence, but it is not always the path every message uses.

Reduce complexity at the source

Remove confirmed retired services. Ask providers whether separate includes are both required. Use an owned CIDR only when the organization truly controls and maintains it. Splitting mail streams across deliberate MailFrom subdomains can make ownership clearer, but it changes identities and must be designed with bounce handling and DMARC alignment.

Avoid copying a provider’s current IP ranges into DNS as a one-time “flattening” fix. That transfers update responsibility to you and can authorize stale ranges or break new ones.

Verify after publication

Query authoritative and recursive DNS, rerun bounded traces, and send fresh tests from each legitimate stream. The goal is not merely a number below ten: it is a maintainable policy whose actual evaluation stays within protocol limits and whose dependencies have accountable owners.