SPF validation summary
{{ domain }}
{{ summarySecondaryLine }}
{{ summaryBadge }} {{ lookupBudgetBadge }} {{ terminalPolicyBadge }} {{ traceBadge }}
SPF validation inputs
levels
domains
Qualifier Term Value Lookup cost
{{ row.qualifier }} {{ row.term }} {{ row.value }} {{ row.lookupCost }}
Check Status Notes
{{ row.label }} {{ row.status }} {{ row.note }}
Depth Via Domain Lookups Notes
{{ row.depth }} {{ row.via }} {{ row.domain }} {{ row.totalLookups }} {{ row.note }}
:

Introduction:

Sender Policy Framework, or SPF, is the DNS policy that tells receiving mail systems which servers are allowed to send mail for a domain. A clean SPF record can reduce avoidable delivery trouble, while a crowded or contradictory record can create lookup overruns, soft policies, or outright configuration errors.

This tool inspects the published SPF data for one domain and turns it into several practical views. It fetches live TXT answers, identifies SPF records, parses the chosen record into mechanisms, estimates both direct and expanded DNS lookup counts, and then summarizes the result through SPF Tokens, Include/Redirect Expansion, Evaluation Checks, and JSON.

That makes it useful before and after DNS changes. You can use it to review a policy before adding another mail provider, to see why an inherited record feels harder to reason about than it should, or to check whether a new include chain is pushing the configuration toward the SPF lookup budget.

Treat the result as record analysis, not proof that a real message will authenticate. The package does not test a live sender IP, SMTP session, HELO identity, DKIM, or DMARC alignment, so a passing checklist is still only one part of email-authentication hygiene.

Everyday Use & Decision Guide:

Enter the domain apex in Domain, not a full URL, mailbox, or host path. example.com is the right shape. https://example.com and user@example.com are not. The package normalizes the domain before lookup, but starting with the right object makes the result easier to trust.

Leave Max expansion depth and Max expansion nodes at their defaults on the first pass unless you already know the policy tree is unusually large. Those controls are there to keep expansion readable and bounded. If the summary later says limits reached, raise them deliberately instead of assuming the estimate is complete.

The most important top-line comparison is Direct versus Expanded lookups. Direct count comes from the root record alone. Expanded count adds the lookup-causing terms found in recursively followed include and redirect children. If the expanded number is close to or above 10, or if the summary says expansion is incomplete, slow down before adding another sender or copying the record elsewhere.

This is a strong fit for policy cleanup, pre-change review, and explaining why a published SPF line feels risky. It is a poor fit for answering whether a specific message really passed SPF at a receiving server, because the package does not simulate a full SMTP transaction or evaluate the sending IP against live message identities.

When the checks panel shows Single SPF record fail, No +all fail, No deprecated ptr fail, or an expansion warning, treat those as configuration problems to investigate before worrying about cosmetic cleanup. The browser performs the DNS-over-HTTPS request directly, so the queried domain is exposed to the external resolver.

Technical Details:

The package begins by normalizing the input domain to ASCII, removing leading or trailing dots, and converting it to lowercase. It then sends a browser-side DNS-over-HTTPS request to Cloudflare's dns-query endpoint for TXT data and measures the elapsed request time shown in the summary.

TXT answers are normalized before SPF parsing. If a TXT answer is split into several quoted strings, the package concatenates those strings into one continuous text value, then filters for records that begin with v=spf1. When multiple SPF records are present, it keeps the full count in spfCount but chooses the longest SPF string as the primary record for tokenization.

Tokenization is simple and deterministic. After removing the leading v=spf1, the package splits on whitespace, captures an optional qualifier such as +, -, ~, or ?, then separates the mechanism name from any value after : or =. The direct lookup estimate increments for include, a, mx, ptr, exists, and redirect.

The expansion model is narrower than a full SPF evaluator on purpose. The package recursively follows only include and redirect references, building a tree of domains, depths, notes, and lookup totals. It marks cycles, invalid domains, missing SPF records, macros that cannot be expanded safely, and truncation from user-set depth or node limits. a, mx, ptr, and exists still count toward lookup totals where they appear, but this package does not recursively resolve those mechanisms into additional DNS data.

The final checks table combines standards-oriented guardrails with package-specific hygiene rules. It tests for one SPF record, a proper v=spf1 start, the presence of a terminator, all placement and qualifier, duplicate all, redirect, or exp usage, deprecated ptr, a 10-lookup ceiling on the expanded estimate, absence of +all, and a record-length check of 255 characters or less. That last length rule is a conservative app check on the concatenated SPF string, not a full DNS wire-size or multi-string-fit calculation.

Rule Core:

  1. Normalize the domain and request TXT data through DNS over HTTPS.
  2. Collect TXT answers, concatenate quoted TXT segments, and keep only strings that start with v=spf1.
  3. Choose the longest SPF string as the primary record while preserving the full SPF-record count for validation.
  4. Parse each token into qualifier, mechanism, and value.
  5. Count lookup-causing mechanisms in the root record to produce lookupCountDirect.
  6. Follow include and redirect references recursively, subject to Max expansion depth and Max expansion nodes, then sum child lookup counts into lookupCountExpanded.
  7. Run the Evaluation Checks list against the parsed record, the record count, and the expansion outcome.
Mechanism and expansion behavior
Mechanism or element Counted in lookup estimate Expanded recursively How the package treats it
include Yes Yes Followed into a child SPF node unless macros, invalid domains, or limits block it
redirect Yes Yes Followed as a child node; duplicate redirect modifiers are flagged in checks and notes
a, mx, exists, ptr Yes No They increase the lookup estimate but are not dereferenced into additional DNS trees
ip4, ip6, all No No Parsed and shown in tokens, but they do not increase the lookup counter
exp No No Not counted for lookups here; the checks panel only verifies that there is not more than one
Evaluation checks
Evaluation check Pass condition in this package Why a fail matters
Single SPF record Exactly one v=spf1 TXT record is found Multiple SPF records are invalid SPF publication and can lead to permanent errors
Includes/redirects could be expanded No macro, invalid-domain, cycle, or truncation problem blocked expansion An incomplete tree weakens confidence in the expanded lookup estimate
Has terminator The parsed record contains all or redirect Without an ending policy, receivers may fall back in ways you did not intend
DNS lookups <= 10 (expanded estimate) The summed expanded estimate stays at 10 or lower Going over budget can make SPF evaluation fail at receivers
No +all The record does not contain an explicit or implicit allow-all ending +all authorizes every sender and defeats SPF protection
Record length <= 255 The concatenated primary SPF string is 255 characters or shorter This app treats longer records as a hygiene warning even though real TXT publication can span multiple strings

The JSON tab serializes the domain, effective limits, TTL, elapsed time, SPF-record count, primary record text, direct and expanded lookup counts, expansion metadata and nodes, parsed tokens, and the full checks list. Copy and download actions stay in the browser, but the DNS query itself is still sent to Cloudflare's resolver.

Step-by-Step Guide:

Use this flow when you want a clean first read of an existing SPF policy or a before-and-after check around DNS edits.

  1. Replace the example value in Domain with the apex domain you want to inspect, then select Validate SPF. If the field is blank, the package will stop with Domain is required.
  2. Open Advanced only if you need to tune tree traversal. Max expansion depth is clamped to 1 through 20 and Max expansion nodes is clamped to 5 through 120.
  3. Read SPF Summary first. The most important fields are the domain, TTL, SPF-record count, Direct and Expanded lookup counts, and any note that expansion was incomplete or truncated.
  4. Open SPF Tokens to inspect the parsed mechanisms. This is the fastest place to confirm the presence of includes, redirect, terminator style, and any obvious +all or ptr problem.
  5. Open Include/Redirect Expansion to see how child domains affect the overall lookup budget. If the notes column mentions macros, invalid domains, cycles, or truncation, treat lookupCountExpanded as incomplete rather than final.
  6. Finish on Evaluation Checks and JSON. Fix structural fails before fine-tuning policy style, then rerun the same domain after DNS changes so the new checks and lookup counts can be compared on the same basis.

Interpreting Results:

The summary number that matters most is lookupCountExpanded, but it is only trustworthy when the expansion status is complete. Read it together with spfCount, the expansion notes, and the pass or fail checks. A low lookup number does not rescue a domain that publishes multiple SPF records or an allow-all policy.

  • A clean checks table does not mean a real message will pass SPF everywhere. This package inspects policy text and include or redirect structure, but it does not evaluate a live sending IP against a live SMTP identity.
  • If spfCount is greater than 1, the tool still chooses one primary record to parse, but that does not make the publication valid. Treat the Single SPF record fail as a blocking configuration issue.
  • If lookupCountExpanded is close to 10, do not treat that as comfortable headroom. One extra include or a provider-side policy change can push the real evaluation over budget.
  • When expansion notes say macros could not be expanded or limits were reached, verify the affected branches manually before deciding the record is safe to keep.

The practical follow-up is usually straightforward: repair duplicate or permissive structure first, then rerun the same domain and compare the checks table and expanded lookup count before you publish more changes.

Worked Examples:

A compact policy with one hosted-mail include

Suppose the root record is v=spf1 ip4:198.51.100.0/24 include:_spf.mail.example -all and the included child publishes v=spf1 a mx -all. The root record yields lookupCountDirect: 1 because only the include counts at the top level. After expansion, the child adds two more lookup-causing terms, so lookupCountExpanded: 3. The checks for Single SPF record, Has terminator, No +all, and DNS lookups <= 10 all pass, which makes this a manageable policy to maintain.

A policy that looks tidy until the tree is expanded

Now imagine a root SPF line with six hosted-service includes and one redirect, where the child records together raise the summed estimate to lookupCountExpanded: 11. The root might still look short and readable in SPF Tokens, but DNS lookups <= 10 (expanded estimate) fails once the expansion tree is added up. That is the point where the tool becomes a change-planning aid rather than a simple parser.

Why the expansion view may say it is incomplete

A domain publishes v=spf1 include:%{d}.mail.example -all. The tokens parse cleanly, so the record still appears in SPF Tokens, but the expansion step cannot safely resolve the macro-based include target. The relevant node receives a note like Unexpanded include (macros), and the check Includes/redirects could be expanded fails. That does not automatically make the SPF line invalid, but it does mean the package cannot present the expanded lookup estimate as complete.

FAQ:

Does a passing checklist mean my mail will authenticate everywhere?

No. The checks here are about published SPF structure and the package's expansion model. They do not test a live sender IP, MAIL FROM identity, HELO identity, forwarding behavior, DKIM, or DMARC alignment.

Why can Expanded be higher than Direct?

Direct counts lookup-causing mechanisms in the root SPF record only. Expanded adds the lookup-causing terms found in recursively followed include and redirect child records, so it is usually the more realistic budget figure.

Does this tool expand a, mx, exists, or ptr lookups?

No. Those mechanisms still count toward the lookup estimate, but only include and redirect are recursively followed into child SPF nodes. That is why the expanded figure is an estimate rather than a complete SPF execution trace.

Why did the tool say there are multiple SPF records?

Because it found more than one TXT answer beginning with v=spf1 for the same owner name. The package picks the longest one to parse so you can inspect something concrete, but the Single SPF record check still fails because duplicate SPF publication is a real problem.

Is my query private?

The site does not add a separate server-side lookup layer for this tool, but the browser still sends the DNS-over-HTTPS request to Cloudflare's resolver endpoint. Treat the queried domain as disclosed to that resolver even though the results stay in client-side state afterward.