# SPF Lookup Budget Checker

> Trace live SPF include and redirect chains, count RFC 7208 DNS lookup terms, and identify syntax, policy, recursion, and resilience problems.

- **Category:** Deliverability
- **Updated:** July 2026
- **Standard:** RFC 7208
- **Canonical page:** [https://emailbump.com/tools/spf-record-checker](https://emailbump.com/tools/spf-record-checker)
- **Interactive tool:** [Open the checker](https://emailbump.com/tools/spf-record-checker)

## What the tool does

The checker accepts either:

- A domain whose published SPF policy should be resolved from live DNS
- A draft root SPF record associated with a domain

In draft mode, the pasted root record remains in the browser while referenced `include` and `redirect` domains are resolved from live DNS. The checker then provides:

- Recursive `include` and reachable `redirect` traversal
- Direct and expanded lookup-term counts for every record node
- A worst-case count against SPF's ten-term evaluation limit
- A lower-bound result when macros, loops, DNS errors, missing records, or safety limits prevent complete traversal
- Duplicate SPF-record detection
- Terminal-policy analysis for `all`
- Warnings for `ptr`, strict budget pressure, unknown mechanisms, duplicate redirects, and unreachable terms
- A term-by-term lookup ledger
- TTL and resolver DNSSEC authenticated-data observations
- Exportable JSON evidence

Live DNS queries are sent from the browser to Google Public DNS over encrypted DNS-over-HTTPS. The request disables EDNS Client Subnet by sending `edns_client_subnet=0.0.0.0/0`. Queried domain names are disclosed to Google's resolver. Email addresses, message content, and Email Bump account data are not involved.

## SPF evaluates the SMTP identity

SPF authorizes client IP addresses to use an RFC5321.MailFrom domain, commonly visible as the Return-Path after delivery. It does not directly authenticate the human-readable display name or the visible RFC5322.From address.

DMARC can use an SPF pass when the SPF-authenticated MAIL FROM domain aligns with the visible From domain. A technically valid SPF policy can therefore still fail to contribute to DMARC when the identities do not align.

## The ten-term DNS lookup limit

RFC 7208 requires SPF implementations to limit the total number of evaluated DNS lookup-causing mechanisms and modifiers to ten. Exceeding the limit produces `permerror`.

The counted terms are:

- `include`
- `a`
- `mx`
- `ptr`
- `exists`
- `redirect`

The following do not consume the ten-term runtime budget:

- The initial TXT lookup that selects the SPF record
- `all`
- `ip4`
- `ip6`
- `exp`, whose explanation lookup happens after policy evaluation

An include or redirect consumes one term itself. The terms evaluated inside the referenced policy also count.

For example:

```text
v=spf1 ip4:192.0.2.10 include:a.example include:b.example -all
```

The `ip4` and `-all` terms cost zero. Each include costs one plus the work evaluated within its child policy.

## Why the checker reports a worst-case graph

SPF mechanisms are evaluated from left to right and can stop when one matches. An include that returns pass can end evaluation before later mechanisms are reached. A particular message can therefore use fewer terms than the complete policy path.

The checker counts the reachable policy graph that may need evaluation when earlier mechanisms do not match. This exposes whether a legitimate message could encounter more than ten lookup-causing terms. It does not predict the SPF result for a particular client IP.

An `all` mechanism always matches. Terms after it are unreachable. A `redirect` modifier is only evaluated if no mechanism matches, so any reachable `all` makes the redirect unreachable regardless of where the modifier text appears in the record.

## Include and redirect are different

### include

```text
include:_spf.vendor.example
```

An include asks whether the referenced domain's SPF evaluation returns pass. It is commonly used to add an external service's authorized sending infrastructure while the original record retains control of the surrounding policy and terminal result.

The include term costs one lookup-causing term, and the referenced policy's evaluated terms add to the same ten-term budget.

If the referenced domain has no SPF record, multiple selected SPF records, invalid syntax, or another permanent error, the include can produce `permerror`; it does not simply mean “no match.”

### redirect

```text
redirect=_spf.shared.example
```

Redirect delegates the policy result to another domain only after every mechanism in the original record fails to match. It is useful when several domains intentionally share a complete policy under the same administrative control.

Redirect costs one lookup-causing term plus the referenced policy. It must not appear more than once. A redirect has no effect when an `all` mechanism is present because `all` always matches first.

## Mechanism accounting

| Mechanism or modifier | Counts toward ten? | Additional considerations |
| --- | --- | --- |
| `include` | Yes | Recursively evaluated child terms also count |
| `a` | Yes | Performs A or AAAA work for the target |
| `mx` | Yes | Looks up MX hosts and their addresses; per-mechanism limits also apply |
| `ptr` | Yes | RFC 7208 says it should not be published |
| `exists` | Yes | Tests a macro-expanded domain for an address result |
| `redirect` | Yes | Child policy terms also count when redirect is reached |
| `ip4` | No | Direct IPv4 comparison |
| `ip6` | No | Direct IPv6 comparison |
| `all` | No | Always matches and terminates mechanism evaluation |
| `exp` | No | Explanation lookup is outside the ten-term evaluation count |

### MX and PTR sub-limits

The ten-term budget is not the only processing limit. During an `mx` mechanism, evaluating each MX record must not produce queries for more than ten address records. Exceeding that limit produces `permerror`.

PTR processing and the `%{p}` macro also have address-work limits. The `ptr` mechanism is slow, complex, and controlled partly by the client IP owner; the RFC says domains should not publish it.

The interactive checker counts the top-level mechanism term and flags `ptr`. It does not expand client-IP-dependent PTR work or pretend a static graph can know a future MX or address answer set.

## Void lookups

A void lookup is a DNS query that returns either:

- A successful DNS response with no usable answers
- A name error such as NXDOMAIN

RFC 7208 says implementations should limit void lookups to two and recommends two as the configurable default. Exceeding the void limit produces `permerror`.

A static policy graph cannot determine every future void lookup. The target of an `exists` mechanism may be macro-expanded using the sender or client IP. `a`, `mx`, and other DNS answers can change between evaluations. The checker flags unresolved policy nodes and macros but does not claim a definitive message-time void count.

## Multiple SPF records

A DNS name must not publish multiple TXT records that independently begin with `v=spf1`. If record selection finds more than one, SPF produces `permerror`.

This is invalid:

```text
v=spf1 include:a.example -all
v=spf1 include:b.example -all
```

The records must be intentionally consolidated:

```text
v=spf1 include:a.example include:b.example -all
```

Consolidation does not create more lookup headroom. The combined graph still has to stay within the ten-term limit.

## Terminal policy and qualifiers

Every mechanism can have a qualifier:

| Qualifier | Result when the mechanism matches |
| --- | --- |
| `+` or omitted | pass |
| `-` | fail |
| `~` | softfail |
| `?` | neutral |

The `all` mechanism always matches, so its qualifier defines the result for clients that matched no earlier mechanism:

- `-all`: unmatched clients fail
- `~all`: unmatched clients softfail
- `?all`: unmatched clients receive neutral
- `+all` or unqualified `all`: every remaining client passes

`+all` usually defeats the authorization policy and is treated as an error by the checker. When neither `all` nor a reachable `redirect` appears, evaluation defaults to neutral. RFC 7208 recommends an explicit ending because it is easier to debug.

## Record size and TXT strings

SPF is published in a DNS TXT record. A single TXT resource record can contain multiple character strings, each limited by DNS representation rules; those strings are concatenated without inserted spaces when consumed.

A DNS control panel may split a long value automatically or require the operator to enter quoted chunks. Splitting one TXT resource record into character strings is different from publishing multiple separate `v=spf1` resource records.

Record growth can also increase response size, transport behavior, and maintenance cost even when every listed `ip4` or `ip6` term is free under the ten-term budget.

## Why policy graphs change without root-record changes

An external include delegates part of runtime authorization to a policy controlled by another operator. The provider may add another include, redirect, `a`, or `mx` term. Your root TXT value and TTL can remain unchanged while the expanded count increases.

For that reason:

- Leave structural headroom rather than treating ten as a comfortable target.
- Recheck after provider, infrastructure, or mail-flow changes.
- Monitor SPF `permerror` in Authentication-Results and DMARC aggregate data.
- Track which team owns each external dependency.
- Remove a provider include when the organization stops using that sender.

## Safer ways to reduce lookup pressure

### Remove unused authorization

Inventory the systems that actually send with the domain's MAIL FROM identity. Remove abandoned vendors, old migrations, duplicate includes, and broad mechanisms that are no longer justified.

### Reduce unnecessary mechanisms

An `a` or `mx` mechanism should exist because those specific hosts send mail, not because it looks conventional. An organization's inbound MX hosts often do not send outbound email.

### Separate independent streams intentionally

Marketing, transactional, support, and employee mail can use intentional MAIL FROM subdomains with policies that reflect their distinct infrastructure. This reduces coupling and blast radius. It must be designed alongside bounce handling, DKIM, DMARC alignment, and provider configuration; simply moving text into a subdomain is not a complete migration.

### Treat flattening as operated caching

Flattening replaces dynamic provider includes with resolved IP ranges. It can reduce message-time lookup terms, but it transfers responsibility for provider changes to the domain owner or flattening service.

A reliable flattening system needs to:

- Resolve every relevant IPv4 and IPv6 dependency
- Respect provider TTL and change cadence
- Refresh before cached authorization becomes stale
- Update DNS atomically
- Preserve the prior known-good record during resolver or provider failures
- Detect unexpected range expansion or contraction
- Alert an owner when refresh or publication fails
- Avoid authorizing stale addresses after a provider removes them

A one-time manual copy of current provider IP addresses is not a durable fix.

## How to use the checker

1. Enter the exact domain used for SPF evaluation.
2. Choose **Live DNS** to inspect its published TXT policy.
3. Choose **Draft record** to inspect an unpublished root value while resolving its dependencies live.
4. Review the worst-case count and whether the total is exact or a lower bound.
5. Fix error findings before interpreting policy strength.
6. Inspect every child node and confirm that the referenced provider is intentional.
7. Review the term ledger to find where the budget is spent.
8. Export JSON when sharing evidence with DNS, security, or vendor owners.
9. Re-run the check after publication and after DNS caches have had time to refresh.
10. Confirm actual messages using Authentication-Results headers and DMARC aggregate reports.

## Limitations

The checker is a static policy-analysis tool. It does not:

- Evaluate a specific SMTP client IP and MAIL FROM address
- Expand message-time SPF macros
- Guarantee the resolver used by a receiving mail system sees identical cached data
- Predict transient DNS errors or total evaluation time
- Fully enumerate client-dependent `ptr` processing
- Prove a future `a`, `mx`, or `exists` query will or will not be void
- Replace Authentication-Results evidence from real receiving systems
- Determine whether an authorized sender is organizationally legitimate
- Prove SPF alignment with a visible From domain without the message and applicable DMARC policy

When any referenced node cannot be resolved completely, the tool reports a lower bound such as `≥7` rather than counting unseen dependencies as zero.

## Frequently asked questions

### Does the initial SPF TXT query count toward ten?

No. The count applies to evaluated `include`, `a`, `mx`, `ptr`, `exists`, and `redirect` terms. Recursively evaluated work triggered by includes and redirects is included.

### Do multiple ip4 terms consume multiple lookups?

No. `ip4` and `ip6` perform direct address comparisons and do not consume the ten-term DNS budget. They can still make a record large and operationally difficult.

### Is ten a safe target?

Ten is a hard ceiling, not a resilience target. A provider can add nested DNS terms inside an include without changing your root record. Leave headroom and monitor the expanded graph.

### Why does the tool show a lower bound?

A loop, missing SPF record, DNS failure, macro-expanded target, multiple record error, or traversal safety limit prevented complete recursive analysis. Known terms still count, but the checker does not assume unseen work is zero.

### Can multiple SPF records increase capacity?

No. Multiple selected SPF records for the same DNS name produce `permerror`. Consolidating the records still leaves one shared ten-term evaluation limit.

### Does an SPF pass mean the visible From address is authenticated?

SPF authenticates the MAIL FROM identity. DMARC requires that identity to align with the visible From domain before SPF can satisfy DMARC. Aligned DKIM can satisfy DMARC independently.

## Primary sources

- [RFC 7208: Sender Policy Framework](https://www.rfc-editor.org/rfc/rfc7208.html)
- [IANA SPF Parameters Registry](https://www.iana.org/assignments/spf-parameters/spf-parameters.xhtml)
- [Google Public DNS JSON API](https://developers.google.com/speed/public-dns/docs/doh/json)

## Related guidance

- [SPF definition](https://emailbump.com/glossary/spf.md)
- [DMARC definition](https://emailbump.com/glossary/dmarc.md)
- [Email authentication definition](https://emailbump.com/glossary/email-authentication.md)
- [DMARC record builder](https://emailbump.com/tools/dmarc-record-builder.md)
- [Sending domains documentation](https://emailbump.com/docs/domains.md)
