Last Black Friday, a major retail customer called us at 2 a.m. because 30% of their US East users were suddenly being routed to our London PoP. Their origin wasn't slow, their edge cache hit ratio was 98%, and yet their p95 Time to First Byte had doubled. The culprit wasn't our edge network or their application — it was a large ISP's recursive resolver in Virginia that had cached a stale, non-ECS-mapped DNS response for six hours straight.
The TTL Tug-of-War You Can't Directly Control
When you set a TTL on your CDN's authoritative DNS record, you're making a suggestion, not giving an order. I've learned this the hard way over a decade of tuning DNS for performance. You might set a 20-second TTL thinking you'll get rapid failover and precise traffic steering, but once that record leaves your authoritative nameserver, its fate is in the hands of thousands of recursive resolvers you don't control. Every ISP, public DNS provider, and even corporate firewall has its own caching logic.
Most engineers assume DNS latency is just the round-trip to the authoritative server, but that's only true on a cache miss. On a cache hit, resolution can be 1-2ms. On a miss that has to traverse the full chain — from stub resolver to recursive resolver to root, TLD, and your authoritative server — you're often looking at 60-120ms before a single TCP packet ever leaves the client. That variance is brutal for CDN performance because it happens before we've had a chance to do any clever routing. If you're pointing your root domain at the CDN, that initial lookup method matters even more, which is why DNS CNAME Flattening: Pointing Root Domains to CDN Without Breaking DNS Rules is often a prerequisite for getting consistent resolution times.
We've also seen resolvers that don't respect low TTLs at all. Some ISP resolvers enforce a minimum TTL of 300 seconds or even 3600 seconds regardless of what you publish, to reduce upstream query load. Others do the opposite and cap your TTL, evicting entries early. You can't fix this by just lowering your TTL to 10 seconds — you'll increase authoritative query load without actually improving propagation. A more reliable approach is to design for this inconsistency and use DNS NS Delegation for CDN: Offloading Domain Resolution to Edge Networks so your CDN's authoritative tier can answer directly and maintain tighter control over records.
; Checking what a recursive resolver actually cached vs. what we publish
; Authoritative answer (what we set):
$ dig @ns1.cdn-provider.net cdn.example.com A
;; ANSWER SECTION:
cdn.example.com. 20 IN A 151.101.65.67
; Cached answer from ISP recursive (5 minutes later):
$ dig @isp-recursive.example.net cdn.example.com A
;; ANSWER SECTION:
cdn.example.com. 287 IN A 151.101.65.67
;; Query time: 2 msec ;; -> CACHE HIT (287s remaining, not 20s)
; A resolver enforcing a minimum TTL will show this:
$ dig @aggressive-isp.net cdn.example.com A
;; ANSWER SECTION:
cdn.example.com. 300 IN A 151.101.65.67
;; Query time: 1 msec ;; -> TTL clamped to 300s floor
How Recursive Caching Breaks Geo-Routing Without EDNS Client Subnet
Here's where caching gets really painful for CDNs. Traditional recursive resolvers cache a single response per hostname and serve that same IP to every client that asks them. So if the first client to query that resolver is in New York, the resolver caches the New York-optimized edge IP. The next client, even if they're in Miami or Chicago and using the same ISP resolver, gets that same New York IP. For a small regional ISP, this isn't a huge deal. For a large, centralized resolver serving millions of users across a wide geography, it's a disaster.
I ran into this exact problem in 2021 with a national ISP that had consolidated its DNS infrastructure into two data centers. Their resolvers in Dallas were answering queries for users from Seattle to Boston with the same cached record. We were doing perfect GeoDNS on our authoritative side, but it didn't matter — the resolver's cache was hiding the client's true location from us. The HTTP Working Group Specifications (https://httpwg.org/specs/) are clear that connection reuse and routing should be as close to the user as possible, but DNS caching was actively working against that principle by pinning users to distant edges.
The fix we rely on now is EDNS Client Subnet for CDN Geolocation: Routing Users to the Nearest Edge, where the recursive resolver forwards a truncated client subnet to our authoritative server so we can make a better decision. But — and this is the catch — not all resolvers support it, and those that do cache responses per subnet, not per client. If your's resolver doesn't send ECS, you're back to that single cached IP problem. We've had to build fallback logic that detects resolver populations without ECS and steers them via Anycast instead of GeoDNS. The articles on web.dev Articles (https://web.dev/articles/) on optimizing TTFB often focus on server and client rendering, but they don't always emphasize that a 50ms DNS penalty from a stale cache wipes out all your frontend optimizations.
# Example authoritative log showing resolver behavior with and without ECS
# Good resolver (with ECS) - we can route accurately:
2024-05-12T14:02:11Z query: cdn.example.com IN A 203.0.113.45
client=71.22.14.8/24 ecs=71.22.14.0/24 -> response: 151.101.129.67 [IAD PoP]
2024-05-12T14:02:18Z query: cdn.example.com IN A 203.0.113.45
client=71.22.98.12/24 ecs=71.22.98.0/24 -> response: 151.101.193.67 [MIA PoP]
# Same resolver IP, different ECS subnet -> different, correct edge
# Bad resolver (no ECS) - one cache entry for all users:
2024-05-12T14:03:01Z query: cdn.example.com IN A 198.51.100.23
client=198.51.100.23 ecs=none -> response: 151.101.65.67 [DFW PoP]
2024-05-12T14:05:44Z query: cdn.example.com IN A 198.51.100.23
client=198.51.100.23 ecs=none -> cache HIT, serving stale DFW IP to BOS user
What We Actually Measure: Cache Hit vs. Cold Miss Costs
You can't improve what you don't measure, and DNS resolver impact is notoriously hard to measure from the server side alone. For the last three years, our team has used Real User Monitoring (RUM) with Navigation Timing API data to separate DNS time from TCP and TLS time. The data is eye-opening. Users hitting a warm cache on a well-connected public resolver see almost no DNS overhead, while users behind a slow or distant ISP resolver pay a consistent tax on every new hostname, especially when we rotate IPs for load balancing.
Don't just rely on your own dig tests from your office network — that only shows you one resolver's view. We've found synthetic tests from multiple resolver vantage points to be essential. A common pattern is that the CDN's authoritative performance looks great (15ms globally), but end-user DNS latency is 3-4x higher because of the recursive layer in between. That's the hidden latency factor most performance dashboards completely miss until they segment by DNS resolver ASN.
The table below is a composite of data we collected across a North American e-commerce property over 7 days in Q1 2024, using RUM data from ~45 million page views. It compares resolution behavior for the same CDN hostname across different resolver populations. We've anonymized the ISPs, but the pattern is consistent across our customer base.
| Recursive Resolver Population | Avg. DNS Resolution Time | Cache Hit Rate (Measured) | Impact on p95 TTFB |
|---|---|---|---|
| ISP Resolver (Warm Cache, No ECS) | 4 - 8 ms | 94% | +6 ms (but 18% misrouted to non-optimal PoP) |
| ISP Resolver (Cold Cache / Miss) | 68 - 112 ms | 6% | +74 ms |
| Google Public DNS (8.8.8.8) with ECS | 11 - 16 ms | 89% | +13 ms (correctly routed) |
| Cloudflare 1.1.1.1 with ECS | 9 - 14 ms | 91% | +11 ms (correctly routed) |
| Corporate / Firewall Resolver (Capped TTL) | 22 - 35 ms | 78% | +28 ms (stale cache for 5-15 min) |
Taming the Cache: Practical TTL and ECS Configurations That Survive the Real Internet
You can't force Google Public DNS or your ISP's resolvers to respect your beautifully tuned 20-second TTL. We've seen resolvers clamp anything under 30s up to 300s, and some enterprise resolvers ignore your TTL entirely and cache for a fixed 5 minutes no matter what you publish. So don't fight a losing battle — design around it. For most dynamic CDN frontends, we've found the sweet spot is a 30-60 second TTL for A/AAAA records on your edge hostnames. It's short enough to let healthy geo-routing and failover actually work, but long enough that you don't get crushed by query volume or penalized by resolvers that floor low TTLs anyway. If you're tempted to go to 10s for "instant" failover, don't — you'll just add authoritative query load without any real-world agility gain.
Where you really win back control is by ensuring every layer honors EDNS Client Subnet (ECS). Without it, that cached mapping in a central resolver in Ashburn, VA will happily serve the same Miami PoP IP to users in São Paulo and Seattle until the TTL expires. With ECS enabled on your authoritative DNS, you get the client’s /24 (IPv4) or /56 (IPv6) prefix and can make a proper nearby PoP decision, even on a cache hit. Most major CDN DNS providers — Route 53, Cloudflare, NS1, Akamai — support it now, but you have to explicitly enable ECS handling in your response logic. And don't forget to sign your responses properly; misconfigured DNSSEC can cause resolvers to SERVFAIL and bypass your cache logic entirely. If you're enabling authenticated responses, see our guide on DNSSEC Signing for CDN Infrastructure: Authenticated DNS Without the Performance Hit to avoid adding 40-80ms of validation latency.
Here's the pattern we deploy for high-traffic hostnames that need both speed and steering. We use a short TTL with ECS-aware split-horizon answers, plus a longer TTL for fallback static assets:
# Terraform example: ECS-aware, short-TTL edge hostname (Cloudflare / Route 53 style)
resource "cloudflare_record" "cdn_edge" {
zone_id = var.zone_id
name = "edge"
type = "A"
ttl = 60 # Don't go below 30s - most resolvers will clamp it anyway
proxied = false
# Enable EDNS Client Subnet on your authoritative provider
# This ensures recursive caches store per-subnet variants, not one global answer
comment = "ECS-enabled dynamic edge - geo-routed per /24"
# On your authoritative side (e.g., NS1, Akamai GTM, or custom),
# ensure your filter chain is: EDNS Client Subnet -> Geolocation -> Active Failover
}
# Verification: Check what resolvers actually cache and return
# dig with +subnet to simulate a client in Brazil vs. Germany
dig @8.8.8.8 edge.example.com +subnet=200.10.10.0/24 +short
dig @8.8.8.8 edge.example.com +subnet=88.10.20.0/24 +short
# If both return the same PoP IP, your authoritative isn't honoring ECS
# Expect ~12-25ms cache HIT vs. 90-140ms cache MISS + geo-lookup
Advanced Patterns and Troubleshooting When Caching Still Hurts
Even with ECS and sane TTLs, you'll still see users pinned to the wrong PoP. The classic symptom? A cluster of users on a single ISP in, say, Jakarta all getting routed to Los Angeles with 220ms TTFB, while the same ISP's users 10 miles away on mobile data hit your Singapore PoP at 18ms. That's almost always a centralized recursive cache without ECS support or a corporate DNS forwarder that strips ECS. The fix isn't DNS — it's Anycast. By announcing the same edge IP from multiple PoPs via BGP AnyCast, you let the network's routing layer correct what DNS caching got wrong. We've measured this as a 35-55% reduction in p95 latency for ECS-stripped traffic, even when DNS still returns a suboptimal IP.
When you're troubleshooting, stop guessing and start measuring cache behavior directly. Don't just `dig` from your laptop. Query the actual resolvers your users use — 8.8.8.8, 1.1.1.1, 9.9.9.9, and the top ISP resolvers in your target region — from distributed probes (RIPE Atlas or Catchpoint are perfect for this). Check the TTL returned: if you publish 60s and consistently get back 285s, that resolver is overriding you. Also look for `OPT` and `ECS` options in the response. An empty ECS scope means the answer is globally cached and not client-specific. And watch for malicious traffic polluting your cache; poisoned or stale entries are often a sign of larger abuse, which is where DNS Sinkholing at CDN Scale: Redirecting Malicious Traffic for Analysis and DNS Amplification Attack Mitigation: Protecting CDN DNS Infrastructure become critical to keeping your DNS path clean and responsive.
Finally, don't forget negative caching. A single NXDOMAIN or SERVFAIL during an origin failover can be cached for 300-900 seconds (the SOA negative TTL), blackholing your entire hostname for users behind that resolver — far longer than your 60s A-record TTL. We always set our SOA `MINIMUM` to 30-45s and ensure our authoritative servers never return NXDOMAIN for edge hostnames, even during failover (return a degraded but valid Anycast IP instead). If you see sudden regional outages that self-heal after 5-15 minutes, negative caching is your culprit 90% of the time.
Frequently Asked Questions
What's the ideal TTL for CDN edge hostnames if resolvers ignore low values?
Don't chase the lowest possible TTL. For dynamic, geo-routed hostnames, 60 seconds is the practical sweet spot for most CDNs. It gives you ~60-90 second global convergence during failover or load shedding, keeps authoritative QPS manageable (we've seen 10s TTLs increase query volume 4-6x with no real gain), and avoids being clamped or penalized by major public resolvers. Use 30s only for hostnames that need sub-minute failover and can handle the load, and use 300s+ for static assets that don't need geo-steering.
Does enabling EDNS Client Subnet completely fix geo-routing issues from resolver caching?
It fixes about 85-90% of them, which is huge. ECS lets supporting resolvers (Google, Cloudflare, OpenDNS) cache per-client-subnet answers, so a user in Berlin and a user in Paris don't share the same cached PoP. But not all resolvers support it — many ISP, corporate, and older resolvers strip or ignore ECS, falling back to a single globally cached answer. That's why you should pair ECS with Anycast routing. DNS gets you most of the way there; Anycast catches the traffic that DNS caching still misroutes.
How can I tell if a resolver is overriding my TTL or not supporting ECS?
Run targeted digs: dig @resolver edge.example.com +subnet=YOUR_PREFIX/24 from multiple client subnets and inspect the returned TTL and answer. If you publish TTL 60 but consistently receive 300, it's being overridden. If two very different subnets (e.g., US and APAC) return the same edge IP and the response lacks an ECS scope option, ECS isn't being honored. For scale, automate this with RIPE Atlas, ThousandEyes, or DNSPerf probes across 1.1.1.1, 8.8.8.8, and regional ISP resolvers to build a real compliance map.
Why are my cache-hit times still 80ms+ even with a 60s TTL?
A cache HIT should be 12-25ms. If it's 80ms+, you're likely measuring the full recursive lookup chain plus network distance to a distant resolver, not the cache itself. Check two things: 1) Is the user forced to a central resolver far from them (common with 8.8.8.8 in parts of Africa/South America)? 2) Is DNSSEC validation failing and causing retries? Also check if the resolver has to do extra CNAME chasing — flatten CNAMEs at the edge where possible. We’ve cut 30-45ms just by replacing a CNAME chain (edge -> lb -> origin) with a direct A record via ALIAS/ANAME.