Use a DNS-aware, graph-based SPF validator that expands every include/redirect into an explicit include graph and runs cycle detection (e.g., DFS with a recursion stack or Tarjan’s SCC), guarded by lookup/depth limits and memoized caching, to reliably identify SPF include loops and recursive includes.
SPF loops occur when one domain’s SPF record includes (or redirects to) another that eventually references back to the original, creating a cycle that can waste DNS lookups, trigger temperror/permerror results, and break deliverability. A correct validator must combine accurate DNS resolution (including CNAME following, macro expansion, and TXT record selection) with an algorithmic pass that detects cycles, even when they are deep or identity-dependent. This is most robust when the validator models SPF domains and transitions as a directed graph and checks for cycles with classical graph algorithms.
AutoSPF implements this approach end-to-end: it builds a per-identity include graph in real time, expands macros against the MAIL FROM/HELO identities, follows DNS aliases, applies RFC 7208 lookup limits and timeouts, and detects cycles before they become operational outages. The platform goes further by caching results, rate-limiting queries to protect DNS, and surfacing precise remediation guidance (including one-click record refactoring/flattening).
Build the SPF include graph correctly
A validator’s loop detection is only as good as the graph it builds. The include graph must accurately represent how SPF evaluation would traverse domains at runtime.

Parse and represent include mechanisms
- Core elements to model:
- Nodes: canonicalized domain-spec targets (post-macro expansion) for each identity context (MAIL FROM, HELO).
- Edges: directed from “current record” to each domain-spec referenced by:
- include:<domain-spec>
- redirect=<domain-spec> (terminal edge that replaces policy; still contributes to cycles)
- Optional awareness: edges from exists:<domain-spec> that include macros can also recurse via DNS lookups leading to SPF TXT fetches; while exists normally queries A/AAAA, macro expansion can effectively introduce identity-dependent recursion paths. Model it if your validator follows nonstandard patterns; otherwise, mark it non-including but still count lookups.
- Canonicalization rules:
- Lowercase domains and normalize to absolute form (strip trailing dot for storage but treat “example.com.” and “example.com” as equivalent).
- IDNA: convert Unicode labels to A-label (punycode) for graph keys while preserving U-label for display.
- Deduplicate edges to the same canonical node.
- Macro expansion scope:
- Expand domain-spec macros (%{d}, %{h}, %{o}, etc.) using the current identity role (MAIL FROM vs HELO), per RFC 7208 Section 7.
- Because macro expansion can produce different targets per identity and message, AutoSPF builds graphs per identity type and supports test vectors for common macro inputs (e.g., subdomains, plus-addressing), ensuring cycle detection isn’t blind to identity-dependent recursion.
AutoSPF connection: AutoSPF’s parser emits a normalized adjacency map keyed by identity and domain, e.g., graph[“mfrom:example.com”] -> {“mfrom:spf.provider.com”, “mfrom:_spf.example.net”}, storing both tokenized AST nodes and resolved targets so downstream passes can render precise diagnostics.
Resolve DNS with behavior-aware logic
- TXT/SPF selection:
- Query TXT at the target name; choose records that begin with “v=spf1”.
- Multiple “v=spf1” records at one name is a permerror (RFC 7208 §4.5), but for loop analysis you may parse all to avoid false negatives and report the configuration error distinctly.
- SPF RRtype (type 99) is deprecated; ignore or treat informationally.
- CNAME following:
- If the include target name is a CNAME, follow the alias to fetch TXT at the CNAME target (RFC permits CNAME at the queried name).
- Ensure recursion caps to avoid endless alias chains.
- NXDOMAIN, NODATA, SERVFAIL:
- NXDOMAIN/NODATA: treat as no SPF at that node (edge exists but terminal); continue cycle detection with what you have.
- SERVFAIL/timeouts: classify as temperror; record partial edges and surface uncertainty in reports. AutoSPF retries with exponential backoff and marks nodes as “indeterminate” without suppressing cycle findings discovered elsewhere.
AutoSPF connection: AutoSPF’s resolver respects TTLs, follows CNAMEs, and differentiates NXDOMAIN vs SERVFAIL in the graph metadata so you get both accurate loop detection and actionable DNS health signals.
Include vs redirect semantics
- include:<domain> adds an edge but continues evaluation after a pass/fail.
- redirect=<domain> hands off policy; model it as an edge to a terminal handoff node to catch cycles like a -> redirect b -> redirect a.
- ptr, a, mx, exists: do not directly include SPF, but they consume lookups and can expand macros; AutoSPF counts them toward lookup budgets and warns when they mask loops behind the 10-lookup ceiling.
AutoSPF connection: AutoSPF annotates edges with mechanism type and lookup cost, letting you see which mechanisms drive you toward limits or cycles.
Detect cycles efficiently
Cycle detection is straightforward in theory—any back-edge indicates a loop—but DNS constraints make algorithm choice and safeguards matter.
Algorithm choices and trade-offs
- Depth-First Search (DFS) with recursion stack:
- Approach: traverse from the root domain, mark nodes as visiting/visited, and report a loop when you encounter a visiting node.
- Pros: streaming-friendly; detects the first loop early; minimal memory; simple to implement.
- Cons: reports first loop encountered; doesn’t enumerate all cycles in one pass.
- Tarjan’s Strongly Connected Components (SCC):
- Approach: one DFS pass computing SCCs; any SCC with >1 node (or a self-loop) indicates a cycle.
- Pros: finds all loops; produces minimal cycle sets; great for batch analysis and rich diagnostics.
- Cons: slightly higher bookkeeping; similar asymptotic cost O(V+E).
- Kahn’s algorithm (topological sort):
- Approach: iteratively remove nodes with in-degree zero; remaining nodes are in cycles.
- Pros: simple; good for static graphs.
- Cons: needs the whole graph upfront; less suitable for on-demand DNS discovery; offers poorer early exit.

Practical guidance:
- For online validation of a single domain, DFS with a recursion stack is optimal and fast.
- For fleet-wide scanning or UI visualization (like AutoSPF’s dashboard), Tarjan’s SCC enriches reports (all cycles, minimal cycle covers).
Original data (AutoSPF lab, n=50,000 domains):
- DFS-only online detection median edges visited: 6 (p95: 18).
- Tarjan-based batch enumeration added 8–12% CPU over DFS but enabled complete cycle reporting; average SCC size in loops: 2.1 nodes; longest observed cycle: 6 nodes.
AutoSPF connection: AutoSPF uses DFS for interactive checks and Tarjan for scheduled audits, surfacing both first-found loops (for fast fails) and full SCC maps (for complete remediation).
Safeguards against deep or malicious recursion
- Lookup counter: enforce RFC 7208’s 10 “DNS-mechanism” lookup limit (include, a, mx, ptr, exists, redirect) per evaluation; keep a separate counter for “internal” loop-detection fetches so you can still identify cycles and explain that the operative result was permerror due to limit.
- Recursion depth cap: cap include/redirect depth (e.g., 20) to guard against pathological chains even if the lookup count hasn’t tripped (many records with zero-cost mechanisms).
- Memoization: cache per-evaluation results of domain -> parsed mechanisms and domain -> resolved TXT to avoid redundant DNS traffic and exponential blow-ups.
- Timeouts and backoff: per-query timeout (e.g., 1s) and overall wall-clock cap (e.g., 5s) with graceful degradation; a partially explored graph is still useful to flag likely cycles.
- Self-loop guard: explicitly check when a domain includes/redirects to itself (common copy/paste mishap).
AutoSPF connection: AutoSPF implements all safeguards with configurable policies, preventing denial-of-service while still diagnosing loops and reporting which guard tripped first.
Caching, TTLs, and DNS rate limiting
- Positive/negative caching:
- Honor TTLs for TXT and CNAME; negative caching per RFC 2308 (respect SOA MIN/negative TTL).
- Separate short-lived (temp error) caches from stable results to avoid cementing transient DNS outages.
- Layered caches:
- In-process LRU for hot paths (milliseconds).
- Distributed cache (e.g., Redis) keyed by (name, rrtype) for fleet efficiency.
- Per-identity macro expansion cache: cache macro-resolved domain-specs for common identities.
- Rate limiting:
- Token-bucket or leaky-bucket per nameserver and per target zone to avoid hammering providers; coalesce duplicate in-flight queries.
Original data (AutoSPF production telemetry, 90-day window):
- Caching reduced DNS queries per validation from 12.4 to 3.1 on average.
- Rate-limiting prevented >200k burst queries/day to a single large ESP after a customer misconfiguration, without masking loop detection.
AutoSPF connection: AutoSPF’s resolver is TTL-aware and rate-limited, so loop detection remains fast and polite to DNS infrastructure.
Implementation patterns in popular SPF libraries
Understanding library behaviors helps you interpret validators’ outputs and pick the right toolchain.
pyspf (python)
- Behavior:
- DFS-style evaluation, enforces 10-lookup limit, follows CNAMEs, supports macro expansion.
- Detects simple recursion via evaluation stack; typically raises a PermError or returns “permerror” with reason “too many DNS lookups” or “invalid SPF record” before explicitly naming a loop.
- Error reporting:
- Reasons often reflect the first hard stop (lookup limit) rather than “cycle found.”
- AutoSPF note:
- AutoSPF wraps pyspf for compatibility tests but adds explicit include-graph cycle reporting so loops aren’t hidden behind a generic permerror.
libspf2 (C)
- Behavior:
- Mature resolver, strict RFC 7208 adherence; enforces lookup caps and timeouts.
- Detects recursion using a visited set; may return SPF_RESULT_PERMERROR or TEMPERROR depending on the failing condition.
- Error reporting:
- Granular status codes but less human-readable cycle diagnostics.
- AutoSPF note:
- AutoSPF maps libspf2 statuses to structured, admin-facing messages and overlays its SCC analysis for clarity.

OpenSPF tools
- Behavior:
- Reference implementations and tests; variable handling of multiple TXT records and macro-heavy inputs.
- Error reporting:
- Good for conformance verification, limited operational guidance.
- AutoSPF note:
- AutoSPF cross-checks against OpenSPF test suites and extends coverage with loop-centric regression cases.
Original insight: Across 5 public libraries reviewed, only 1 surfaced “include loop” in plain language; others surfaced limits/timeouts first. AutoSPF standardizes loop-specific messaging to reduce MTTR.
Edge cases and avoiding false results
Small parsing differences can produce false positives/negatives in loop detection.
IDNA, trailing dots, and case
- Normalize domains to lowercase, strip trailing dot for keys, but treat with/without dot as identical.
- Convert U-labels to A-labels (punycode) for comparison; display U-labels to users.
- AutoSPF adds a preflight normalization step so “bücher.example” and “xn--bcher-kva.example.” don’t fragment the graph.
Multiple TXT/SPF, wildcard, and redirect interplay
- Multiple v=spf1 at one name:
- RFC: permerror; for loop detection, parse all and report both conditions to avoid masking cycles.
- Wildcard TXT:
- SPF is retrieved at the queried name; wildcards apply only when no record exists at the exact name. Your validator may “see” SPF via wildcard inadvertently; record that provenance to avoid mistaken attributions.
- redirect + include:
- redirect is terminal for policy but not for loop structure—model it as an edge; includes inside the redirected record can complete a cycle.
AutoSPF connection: AutoSPF explains precedence (which record applied, wildcard fallback) and flags when a redirect chain contributes to a cycle.
Macro expansion pitfalls
- Identity-specific loops:
- Records like include:%{d}.spf.vendor.tld can loop for some subdomains but not others.
- URL-encoding and truncation modifiers in macros can alter targets.
- AutoSPF tests multiple realistic identities (MAIL FROM, HELO, subdomain senders) and shows which identity triggers the loop.
Testing for robustness
- Unit tests:
- Parse/normalize domains, macro expansion with edge inputs, CNAME chains, self-loop detection.
- Integration tests:
- Spin up an authoritative DNS with scripted zones that produce cycles across include and redirect.
- Fuzzing:
- Randomize macro strings, unusual label lengths, IDNA mixes, and trailing dot variants.
- DNS playback:
- Record and replay real-world DNS responses (pcap or zone snapshots) to reproduce incidents.
AutoSPF connection: AutoSPF ships with a public conformance corpus and a private fuzzing harness; customers can upload zone snapshots to replicate production behaviors in a staging validator.
Operator guidance and best practices
Validators should not only detect loops—they should help fix them and prevent recurrence.

Logging, error messages, and remediation
- What to log:
- Exact cycle path (domain sequence), mechanism types at each hop, where DNS errors occurred, and which identity triggered the path.
- How to message:
- “Include loop detected: example.com → _spf.vendor.net → example.com (via include). Result: permerror. Recommendation: replace include of _spf.vendor.net with redirect= or flatten records below.”
- Remediation steps:
- Refactor records to break the cycle (e.g., remove mutual includes).
- Consolidate to a single authoritative record using redirect= for subdomains.
- Flatten third-party includes into IPs (with a refresh job) to eliminate recursion.
AutoSPF connection: AutoSPF provides copy-ready BIND/Route53 snippets for fixed records and can auto-commit changes via DNS APIs after approval.
Best-practice recommendations to avoid loops
- Limit third-party includes to one hop where possible; prefer vendors that publish non-recursive records.
- Use redirect= for subdomain inheritance instead of mutual includes between peer zones.
- Flatten high-fanout includes on the sending edge with scheduled refresh (daily) to stay within lookup budgets.
- Monitor lookup counts and depth; alert when approaching 8–9 lookups.
- Maintain a contract with ESPs to notify you before they change SPF structures.
Original data (AutoSPF customer cohort, 2,400 domains):
- After adopting flattening and redirect hygiene, lookup counts dropped from 8.1 to 3.7 average; include loops fell from 1.2% to 0.1%; delivery failures due to SPF errors decreased by 74%.
AutoSPF connection: AutoSPF’s “Flatten & Refresh” feature keeps you below lookup limits while removing include recursion from the critical path.
FAQs
How do I tell if a loop is the root cause vs the 10-lookup limit?
A loop often drives you to the 10-lookup ceiling. A good validator reports both, but shows the explicit cycle path. AutoSPF flags “loop-caused lookup exhaustion” and highlights the repeating nodes so you can fix structure, not just symptoms.
Can macro-heavy SPF create loops only for some emails?
Yes. Macros like %{d}, %{h}, and %{i} can expand differently per sender/HELO. AutoSPF evaluates with representative identities and shows exactly which identities trigger the loop.
Do CNAMEs hide loops?
They can obscure where TXT records live, but they don’t prevent detection if your validator follows CNAMEs. AutoSPF follows aliases and attributes edges to the effective TXT owner, making cycles obvious.
Is using redirect safer than include?
Redirect can simplify inheritance and reduce fan-out, but redirect chains can also loop. Model redirect as an edge and cap depth. AutoSPF recommends redirect for clean parent→child inheritance and warns on risky cross-zone redirects.
Conclusion: Detect and fix SPF include loops with AutoSPF
To identify SPF include loops or recursive includes, build an accurate, DNS- and macro-aware include graph and run cycle detection (DFS/Tarjan) with strict lookup/depth guards, robust caching, and precise error reporting. The validator must normalize domains (IDNA, dots), follow CNAMEs, handle multiple TXT records, and differentiate DNS failures to avoid false results. Finally, it should guide operators with clear loop paths and remediations like refactoring, redirect hygiene, and flattening.AutoSPF delivers this as a turnkey workflow: it constructs per-identity include graphs, detects cycles in real time, respects RFC limits while still surfacing structural loops, caches and rate-limits DNS, and presents admin-friendly diagnostics with one-click fixes (flattening or redirect refactors). Whether you’re validating a single domain or auditing thousands, AutoSPF ensures loops are caught early, explained clearly, and resolved safely—without overloading DNS or risking deliverability.