| 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 }} |
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.
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.
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.
v=spf1.lookupCountDirect.Max expansion depth and Max expansion nodes, then sum child lookup counts into lookupCountExpanded.Evaluation Checks list against the parsed record, the record count, and the expansion outcome.| 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 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.
Use this flow when you want a clean first read of an existing SPF policy or a before-and-after check around DNS edits.
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.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.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.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.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.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.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.
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.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.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.
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.
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.
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.
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.
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.
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.
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.
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.