· Paul Crossland
Resolver View Is Fetch State
Split-horizon DNS and resolver exceptions can change crawl results before HTTP begins. Log DNS provenance with every fetch.
A production crawler can do everything right at the HTTP layer and still fetch the wrong thing because the DNS answer came from the wrong view.
That sounds like an infrastructure footnote until it becomes a data incident: one worker sees a public login page, another sees an internal app, a third gets NXDOMAIN, and the extractor team starts tuning selectors because the rendered HTML differs. The URL did not change. The browser did not change. The session cookie did not change. The resolver path did.
The current reminder is Cloudflare's July 20 announcement that Cloudflare Internal DNS is generally available. The product is for private DNS across corporate networks, but the operational lesson is broader for web-data systems: DNS is not a single global truth. It can be intentionally scoped by network, private zone, gateway policy, search domain, resolver configuration, and security exception.
A second fresh reminder came from Cloudflare's July 14 writeup on the .al DNSSEC rollover incident and Extended DNS Error 33. That post focused on a public TLD failure and a resolver-side Negative Trust Anchor signal. Together, these two releases point at the same production risk from opposite directions. Sometimes a different answer is expected because the caller is inside a private namespace. Sometimes a different answer is an emergency resolver exception. In both cases, fetch logs that begin at GET https://example.com are already missing the evidence needed to explain the result.
This is not a bypass topic. Do not use resolver variance to reach private services, evade policy, or work around access controls. The useful lesson for Better Fetch-style operators is defensive: make DNS provenance observable so crawls are reproducible, incidents are diagnosable, and jobs do not silently mix incompatible network views.
The repeated angle to avoid
Recent Better Fetch posts have already covered browser version drift, TLS groups, DNSSEC outages, WAF release windows, session evidence, and locale state. The angle to avoid is another generic "log more context" article that treats every network difference as the same class of problem.
The narrower thesis here is: resolver view should be modeled as part of the fetch input contract, because split-horizon DNS, private zones, and resolver-side exceptions can produce valid but incompatible crawl populations before HTTP, browser automation, cookies, or extraction code run.
That matters most when a crawler fleet spans office networks, cloud regions, customer VPCs, VPNs, Kubernetes clusters, serverless jobs, and browser workers that do not all inherit the same resolver behavior.
Source map
| Source | Date | What it contributes |
|---|---|---|
| Cloudflare Internal DNS is now generally available | 2026-07-20 | Fresh evidence that private DNS views are becoming first-class managed infrastructure, not ad hoc host-file trivia. |
| Cloudflare on .al DNSSEC NTA and EDE 33 | 2026-07-14 | Fresh evidence that resolvers may intentionally change validation behavior during incidents and expose that through Extended DNS Error metadata. |
| RFC 8914: Extended DNS Errors | 2020 | Background for treating resolver explanations as structured diagnostic evidence when available. |
The surplus is in the operational synthesis: private DNS views and emergency DNS validation exceptions are usually managed by networking or security teams, while crawl incidents are debugged by data engineers. A useful fetch platform has to join those worlds in the fetch record.
Where resolver view leaks into crawl quality
DNS differences do not only cause obvious connection failures. They can create valid responses that pass superficial health checks.
A private answer can route app.example.com to an internal application that has different authentication, consent, robots policy, language defaults, feature flags, or API origins than the public property. A public answer can route the same host through a CDN with bot-management, caching, image optimization, and regional edge logic. A resolver exception during an incident can make one region resolve while another correctly fails validation. A Kubernetes or serverless environment can add search domains or resolver caching that a browser worker on a different node never sees.
The dangerous symptom is a mixed dataset: half the records were fetched from one DNS population and half from another, but all rows share the same canonical URL. Downstream, that looks like parser drift, duplicate products, missing prices, inconsistent screenshots, or random authorization failures.
Minimum DNS provenance to log
Add a DNS block to each fetch attempt, not only to connection errors. It should be small enough to keep on hot paths and rich enough to answer "which name did we resolve, through whom, and why?"
| Field | Why it matters |
|---|---|
requested_url and effective_host | Separates the user-facing URL from redirected or canonicalized hosts. |
resolver_profile | Names the configured resolver group, such as public-cloudflare, vpc-prod, customer-vpn, or browser-default. |
resolver_addresses_hash | Lets operators compare resolver configuration without storing sensitive infrastructure details. |
network_view | Records whether the worker expected public Internet, VPC, VPN, office, customer, or test-lab resolution. |
dns_rcode | Distinguishes success, NXDOMAIN, SERVFAIL, and policy-like failures. |
answer_family | Captures A, AAAA, CNAME, HTTPS/SVCB, or no-address outcomes. |
answer_set_hash | Groups equivalent answers without logging every private IP. |
ttl_observed | Helps explain cache persistence after zone or policy changes. |
dnssec_validation_state | Records validating, insecure, bogus, disabled, or unknown when your resolver exposes it. |
extended_dns_error | Captures EDE codes and text where available; treat as evidence, not a universal guarantee. |
resolution_duration_ms | Separates resolver slowness from origin or browser slowness. |
cache_source | Identifies OS cache, browser cache, local forwarder, recursive resolver, or application cache when known. |
Do not log raw private IPs or customer-internal names unless you have a clear retention and access policy. Hashing answer sets is often enough to correlate populations while reducing exposure.
Tests that catch split-horizon mistakes
A crawler should not discover resolver variance for the first time during an incident. Add fixtures that exercise the resolver layer explicitly.
- Public baseline: resolve and fetch a small set of public control domains from every worker class and region. Compare answer-set hashes, HTTP status, final URL, and content fingerprint.
- Private-view fixture: if you intentionally support customer VPC or VPN crawling, use a harmless internal test hostname and assert that only the expected worker pool can resolve it.
- Negative fixture: keep at least one name that should not resolve from the public fleet. Alert if it starts resolving, because that may mean a resolver profile changed.
- DNSSEC fixture: where validation matters, include a domain or lab zone that can distinguish validated, insecure, and bogus outcomes without touching production targets.
- Browser parity check: compare the DNS evidence captured by HTTP-only fetch workers with browser automation workers. Browsers, containers, and host OS settings can disagree.
- Redirect chain check: log resolver evidence for every host in a redirect chain, not just the first URL. A public landing page can redirect to a regional or private host.
These tests should run on deploy, on resolver-profile changes, and after network migrations. They are especially valuable before scaling a new region or customer-specific crawl route.
Triage flow for DNS-shaped incidents
When a crawl changes suddenly, resist the impulse to start in the DOM. Use this sequence first:
- Group failures by
resolver_profile,network_view, andanswer_set_hash. - Compare the same URL across at least two resolver views that are expected to be equivalent.
- Check whether the answer changed before the HTTP status changed.
- Look for
SERVFAIL,NXDOMAIN, DNSSEC validation state changes, or EDE metadata. - Compare TTLs to the timing of zone, CDN, VPN, or gateway changes.
- Only then inspect headers, cookies, browser state, rendered HTML, and selectors.
If the answer-set hash divides the incident cleanly, treat it as a routing or resolver issue until proven otherwise. If the DNS evidence is identical across populations, move up the stack to TLS, HTTP, session, WAF, browser, and extraction diagnostics.
Trade-offs and failure modes
The main trade-off is cardinality. DNS answer sets can change frequently on large CDN-backed sites. Hash the normalized answer set, bucket TTLs, and sample full answers only for incident windows or synthetic fixtures.
The second trade-off is privacy. Internal DNS names and private addresses can reveal sensitive architecture. Prefer labels such as network_view: customer-vpc and stable hashes over raw values in broad logs. Store detailed resolver traces behind tighter access controls.
The third trade-off is false certainty. Application-level DNS logs are only as good as the layer that captures them. A browser may use host caches, encrypted DNS settings, proxy resolution, or platform APIs that your HTTP client does not. That is why browser parity checks matter. The goal is not perfect omniscience; it is enough provenance to know when two fetch attempts were not actually comparable.
Operator decision rule
Before comparing two crawl results, ask: did they share the same URL, browser/session contract, and resolver view?
If resolver view is unknown, do not treat content differences as extraction regressions. If resolver view differs intentionally, partition the dataset and label it. If resolver view differs unexpectedly, pause scale-up and investigate before retrying harder.
DNS is upstream of every browser-grade fetch. Treat it that way in your logs, tests, and incident reviews.