Back in 2018, I was on call when a retailer switched CDN providers the night before Black Friday and set their DNS TTL to 24 hours without thinking about it. The old edge IPs kept getting hammered for a full day, users saw stale content, and we couldn't steer traffic away even though the new CDN was ready. That midnight scramble taught me more about time to live than any RFC ever did.
How DNS Caching Actually Controls Your CDN Performance
Think of DNS TTL as an expiration date you stick on a DNS answer. When a user asks for cdn.example.com, your authoritative DNS tells their resolver, "This answer is good for X seconds." For those X seconds, every resolver and browser in the chain caches it and won't ask you again. That caching is what keeps DNS fast, but it's also what makes changes feel slow if you pick the wrong number.
I've seen beginners assume a low TTL is always better because it feels fresher. It isn't. Every time the cache expires, that next user pays the cost of a full DNS lookup - often 20 to 80ms extra before they even start connecting to your edge. Multiply that by millions of requests and you're adding latency and DNS query costs for no real benefit. On the flip side, I've also seen teams set a TTL of 86400 seconds because someone told them it would "reduce load," then panic when they needed to fail over an edge PoP and nothing moved.
For CDN setups, you're balancing two things: how often resolvers re-query you so you can send them to the best edge, and how often users can skip that lookup entirely. If you're using DNS CNAME Flattening: Pointing Root Domains to CDN Without Breaking DNS Rules for your apex domain, that TTL matters even more because the flattened record is what every resolver caches for your root.
; Simple zone file showing TTL in seconds
$TTL 300 ; default 5 minutes for all records
@ IN SOA ns1.example.com. admin.example.com. 2024010101 3600 600 86400 300
cdn IN A 203.0.113.45 ; inherits 300s TTL
cdn IN A 203.0.113.46 ; you can also set per-record TTLs
; Per-record override for a more dynamic endpoint:
api IN 60 A 203.0.113.99 ; 60s TTL for faster steering
; What a resolver sees:
; $ dig cdn.example.com
; cdn.example.com. 300 IN A 203.0.113.45
The Pain of Too Low vs. Too High: TTL Optimization in Practice
When I ran edge operations for a video streaming platform, we experimented with a 10-second TTL because we wanted perfect load balancing. It worked - we could drain a PoP in seconds - but our authoritative DNS query volume went up 120x overnight and our bill followed. Worse, some ISP resolvers don't respect TTLs under 30 seconds and just round them up anyway, so we were paying for freshness we weren't actually getting. We also saw slower Time to First Byte for users on high-latency mobile networks because DNS caching wasn't helping them at all.
Going too high creates a different headache. We had a customer set a 12-hour TTL for their entire CDN hostname. When one of our European PoPs had packet loss, we wanted to pull it from DNS in under a minute. We couldn't. Users whose ISPs had cached the bad IP kept trying it for hours, and we had to wait out the cache. That is the core tradeoff of TTL optimization: short TTLs give you agility but cost you performance and money, long TTLs give you speed and cache efficiency but take away your steering wheel.
This is where your DNS architecture matters. If you've delegated a subdomain to your CDN via DNS NS Delegation for CDN: Offloading Domain Resolution to Edge Networks, your CDN's authoritative servers are handling those queries directly and can handle higher query rates from low TTLs more gracefully. But if you're still on a single provider's DNS without that delegation, a 30-second TTL across your whole domain can overwhelm your name servers. I always tell teams to decide TTL per hostname, not per domain.
# Checking your current TTL and resolver behavior
# Use dig to see the remaining TTL (second field)
dig cdn.example.com A +nocmd +noall +answer
# Example output - notice the TTL countdown:
# cdn.example.com. 187 IN A 203.0.113.45 (187s left of original 300s)
# Test how EDNS Client Subnet interacts with your TTL
# Low TTLs let you re-route users faster based on subnet location
dig @8.8.8.8 cdn.example.com +subnet=203.0.113.0/24 +short
dig @8.8.8.8 cdn.example.com +subnet=198.51.100.0/24 +short
# Compare with guidance from https://httpwg.org/specs/ and https://web.dev/articles/ on caching and connection setup
Finding Your Sweet Spot: Production-Tested TTL Ranges That Work
After years of tweaking this for different traffic patterns, I don't believe in a single magic number. For most beginner CDN setups, you want to split your hostnames by how often they need to change. Your main static assets - images, JS, CSS on cdn.example.com - can comfortably live at 300 to 600 seconds. That gives you a 5 to 10 minute failover window, which is fast enough for most outages, while still keeping your DNS caching hit rate above 90%.
For dynamic or geo-steered hostnames, go shorter. I typically run API endpoints or personalized edge routing at 60 seconds. And yes, you can be affected by how resolvers handle geolocation. If you're using EDNS Client Subnet for CDN Geolocation: Routing Users to the Nearest Edge, a shorter TTL lets you re-map users to a closer PoP much faster when conditions change. We saw a 15% improvement in p95 latency for our APAC users after dropping our geo-routed hostname from 3600s to 60s, even though query volume rose.
Don't guess - measure. Start at 300 seconds, watch your DNS query volume, cache hit ratio from your CDN analytics, and your failover time during a test drain. The table below is based on numbers I've actually seen across three CDNs handling 5 to 50k RPS, not lab tests. Use it as a starting point for your own TTL optimization experiments.
| DNS TTL Value | Typical Cache Hit Ratio | Effective Failover / Propagation Time | Authoritative Queries (per 1M users/hour) | Best For |
|---|---|---|---|---|
| 30 seconds | 58 - 65% | 30 - 60 seconds | ~115,000 | Emergency steering, live video, active DDoS mitigation |
| 60 seconds | 78 - 84% | 60 - 90 seconds | ~58,000 | Geo-routed API, EDNS-based routing, frequently scaled edges |
| 300 seconds (5 min) | 91 - 94% | 5 - 7 minutes | ~11,500 | Default for most CDN hostnames, best balance for beginners |
| 3600 seconds (1 hour) | 97 - 98.5% | 60 - 75 minutes | ~1,000 | Stable static assets, low-change origins, cost-sensitive setups |
| 86400 seconds (24 hours) | 99%+ | Up to 24 hours | ~45 | Only for truly static records you never need to move quickly - risky for CDN edges |
Implementing Adaptive TTLs Without Breaking Production
Look, you don't want a single TTL for your entire zone — that's how you end up either melting your authoritative DNS or waiting four hours to fail over. What we've settled on in production is a tiered approach. Your apex and static assets like cdn.example.com that point to anycasted edge IPs can comfortably sit at 1800-3600 seconds. They're not moving unless you're re-architecting your PoPs. But anything dynamic — api.example.com, origin failover records, or latency-based routing records — needs to be 60-120 seconds. We've seen teams try to use 300s across the board as a compromise, and all they get is the worst of both worlds: not fast enough for failover, not long enough to get a decent cache hit ratio.
The trick is deploying it without causing a thundering herd. Don't just drop your TTL from 86400 to 60 in one change — you'll see your authoritative query volume spike 18-22x within minutes as every resolver's cache expires. Instead, step it down gradually. Lower to 3600, wait out the old TTL, then to 600, then to your target. And monitor your cache hit ratio at the recursive layer, not just your authoritative QPS. A healthy CDN DNS setup should see 85-92% of queries answered from cache with a 300s TTL; if you drop to 60s and that falls below 65%, you're paying for it in latency — we've measured an extra 35-50ms p95 for cache misses hitting authoritative. Also, remember that your TTL strategy interacts directly with your security posture. If you're running DNSSEC Signing for CDN Infrastructure: Authenticated DNS Without the Performance Hit, shorter TTLs mean more frequent RRSIG validations, so factor in that CPU overhead on your resolvers.
# Terraform - Tiered TTL strategy for Cloudflare / Route 53
# Stepped rollout: don't apply 60s directly if old TTL was 86400
resource "cloudflare_record" "static_edge" {
zone_id = var.zone_id
name = "cdn"
type = "A"
value = "192.0.2.10"
ttl = 1800 # Long-lived anycast edge - stable, high cache hit
proxied = false
}
resource "cloudflare_record" "api_dynamic" {
zone_id = var.zone_id
name = "api"
type = "A"
value = "192.0.2.20"
ttl = 60 # Short TTL for fast failover & latency-based steering
proxied = false
}
resource "cloudflare_record" "origin_failover" {
zone_id = var.zone_id
name = "origin"
type = "CNAME"
value = "origin-failover.example.net"
ttl = 60 # Critical for 90-second origin cutover
proxied = false
}
Once that's live, validate it the way resolvers actually see it, not just what you configured. Use dig +trace +ttlunits and check the remaining TTL from multiple vantage points — we run checks from 8-10 global resolvers (Google, Cloudflare, plus a few ISP resolvers). If you're seeing inconsistent TTLs, you've likely got a stale record stuck in an intermediate cache that didn't honor your step-down. That's when you'll want to have DNS Sinkholing at CDN Scale: Redirecting Malicious Traffic for Analysis patterns in mind too, because sinkholed records with long TTLs are a nightmare to purge during an incident.
Advanced Patterns: Anycast, Stale Cache, and Debugging When TTLs Lie
Here's where it gets messy. Even with perfect TTLs, the internet doesn't always respect them. We've caught major ISP resolvers in Southeast Asia and parts of the EU clamping any TTL under 300s up to 300s, and some actually floor it at 600s regardless of what you publish. If you're relying on a 60-second failover, you don't really have a 60-second failover for about 8-12% of your user base. The workaround isn't to fight it — it's to design for it. Use anycast for your edge so the IP doesn't need to change at all, and reserve short-TTL DNS failover only for origin steering where anycast won't help. And enable stale-while-revalidate / serve-stale on your authoritative side if your provider supports it (Route 53, NS1, and Cloudflare all do now). That lets resolvers serve an expired record for an extra 30-45 seconds while they refresh in the background, which cuts SERVFAILs by over 90% during authoritative blips.
Negative caching will also bite you if you're not careful. The SOA negative TTL (the last field in your SOA record) controls how long NXDOMAIN is cached, and most folks leave it at 3600 or even 86400. So if you create a new subdomain new-api.example.com on the fly and a client queries it a second before it exists, that NXDOMAIN gets cached for an hour. We've learned to set our negative TTL to 60-120 seconds explicitly, even when positive TTLs are longer. It's a one-line SOA tweak that saves hours of debugging "but I just created the record!" tickets.
When things go wrong, don't just stare at your DNS provider's dashboard. Run dig @8.8.8.8 api.example.com +norecurse and dig @1.1.1.1 api.example.com +norecurse to see the TTL remaining, then compare it to dig @your.authoritative.ns. If the remaining TTL never drops below, say, 240s, you've found a resolver that's rewriting your TTL. Check your EDNS Client Subnet (ECS) scope too — a /24 ECS prefix with a 60s TTL can explode your cache fragmentation by 200-300x. And if you're seeing sudden spikes in authoritative load that don't correlate with TTL changes, check for DNS Amplification Attack Mitigation: Protecting CDN DNS Infrastructure — we've seen amplification attacks masquerade as "TTL too low" problems because the QPS pattern looks identical.
Frequently Asked Questions
What's the best TTL for CDN failover records?
60 seconds is the sweet spot for most teams. It's short enough to cut over 85% of your traffic within 90-120 seconds, but not so short that you drown your authoritative DNS. We've tested 30s and 10s in production — you get diminishing returns below 60s because so many ISP resolvers clamp the minimum to 60-300s anyway. Pair it with health checks that run every 10-15 seconds and you'll achieve a 90-second RTO without paying for 10-second TTL overhead.
Will a low TTL hurt my Time to First Byte (TTFB)?
Yes, if you apply it everywhere. With a 60s TTL, expect your DNS cache hit ratio to drop to 60-70% versus 90%+ at 3600s. That means 30-40% of requests pay an extra 20-60ms for a full recursive lookup. That's why you tier it — keep your edge A/AAAA records at 1800s+ (TTFB impact <5ms) and only use 60s where you actually need agility. Don't make your logo's CNAME pay the price for your API's failover needs.
Why are my changes not propagating even after the TTL expired?
Three culprits, in order: 1) You queried before the new record existed and the negative cache (SOA MINIMUM field) is holding an NXDOMAIN for an hour. 2) An ISP resolver is ignoring your TTL and enforcing its own 300-600s floor. 3) Your application or OS is caching DNS independently — Java's JVM, for example, caches forever by default unless you set networkaddress.cache.ttl=60. Always flush and test with dig directly against your authoritative nameserver first to isolate where the stale record lives.
Should I use different TTLs for DNSSEC-signed zones?
You can, but keep your RRSIG validity periods aligned. If your TTL is 60s but your RRSIGs are valid for 7 days, resolvers will still have to re-validate after the TTL expires. We've found a 300s TTL with 24-hour RRSIGs and 12-hour re-signing is a good balance — it keeps validation overhead low while still allowing reasonably fast changes. If you need 60s agility on a DNSSEC zone, automate re-signing every 30 minutes or you'll start serving expired signatures to the 10-15% of resolvers that strictly validate.