You've heard it a hundred times: "Just add a CDN and set a long TTL and your site will fly." That's wrong. Not just slightly wrong—dangerously wrong. Caching isn't a set-and-forget switch. It's a system of trade-offs, and the people who treat it like a light switch are the same ones wondering why their "cached" site still crawls. Let's bust the myths and give you the blunt truth.
Myth: Cache Eviction Policies Don't Matter Much
You might think any eviction policy will do, as long as you have one. But the policy you choose directly shapes your hit rate and your user experience. Redis, the most common distributed cache, gives you a menu: noeviction, allkeys-lru, allkeys-lfu, volatile-ttl, and others (Redis). The lazy default is noeviction, which just errors when memory fills—that's a crash waiting to happen. The blunt advice: use allkeys-lru (least recently used) unless you have a reason not to. Why? Because a cache is not storage; it's a performance layer. You want to keep the hottest data, and LRU does that well. If you have a workload where some keys are accessed very frequently but not recently, allkeys-lfu (least frequently used) might be better—Redis has supported it since 4.0 and it can improve hit ratios (Redis). Don't just leave the default; think about your access patterns.
Myth: Longer TTLs Are Always Better
Longer TTLs mean more cache hits, right? Not if your data changes. Imagine you set max-age=604800 (7 days) on a product page that has a price update tomorrow. You'll serve stale prices for a week (MDN). TTL is a freshness contract, not a performance knob. The Cache-Control header is your lever: max-age tells caches how long the response is fresh, s-maxage overrides for shared caches, and no-cache doesn't mean "don't store"—it means "store but revalidate before every use" (MDN). The real art is balancing freshness and hit rate. For dynamic content that changes often, a short TTL or revalidation is better. For static assets like images, a long TTL is fine. And if you're worried about stale data during revalidation, use stale-while-revalidate from RFC 5861—it lets a cache serve a stale response for up to a specified time while it fetches a fresh copy in the background (RFC 5861). That's the best of both worlds.
Myth: You Need to Invalidate the Whole Cache on Every Update
When you change a product price, do you purge your entire CDN cache? If you do, you're throwing away perfectly good cached content and hammering your origin. The smarter move is to purge only the affected URL. Cloudflare, for instance, offers purge by single-file, which instantly removes just that resource from all edge locations (Cloudflare). Similarly, HTTP caching has a built-in mechanism: when a client sends an unsafe request (like POST, PUT, DELETE), caches must invalidate the stored response for that URI (RFC 9111). So you don't need to nuke everything—just the updated resource. And for dynamic pages that change frequently, consider using no-cache plus validators like ETag or Last-Modified so caches can revalidate cheaply without downloading the whole body (MDN).
Myth: A CDN Is a Magic Bullet for Performance
CDNs are great—they cache content closer to users and can absorb 70% or more of your origin requests (AWS). But they're not a substitute for a well-designed caching strategy. A CDN is just one layer in a stack that includes browser cache, edge cache, load balancer cache, application cache, and distributed cache (AWS). If your origin server is slow, the CDN can only help so much. Also, not all content is cacheable. Static content like HTML files and images can be cached for set periods (Cloudflare), but dynamic content—personalized or frequently changing—often can't. And CDN defaults can surprise you: Cloudflare, by default, caches 200, 206, and 301 responses for 120 minutes, but 404s only for 3 minutes (Cloudflare). You need to configure your TTLs and cache keys carefully, especially if you serve content that varies by user, language, or device. That's where the Vary header comes in—it tells caches to store separate versions based on request headers like Accept-Encoding (MDN).
Myth: Load Balancers and Caches Are Separate Concerns
They're not. Your load balancer can be a cache too, and your cache can affect how you load balance. For instance, nginx can cache proxied responses using proxy_cache_path and proxy_cache_valid (Nginx). That means you can cache at the edge, right where requests come in, before they hit your app servers. And if you're using a load balancer with hashing—like nginx's hash key [consistent]—you can improve cache hit ratios by consistently sending the same user to the same backend, so each backend's local cache is more likely to be warm (Nginx). This is especially important if you're running a distributed cache like Redis, where a consistent hashing scheme can minimize cache misses when you add or remove nodes (Nginx). So don't think of caching and load balancing as separate silos. They work together.
Myth: Cache Invalidation Is Simple: Just Delete the Key
In a distributed cache like Redis, invalidating a key is easy—DEL it or let it expire. But the real challenge is coordinating invalidation across multiple layers. If you have a CDN, a load balancer cache, and a Redis cache, you need to invalidate all of them when data changes. That's where things get messy. The HTTP spec gives you tools: unsafe requests invalidate the target URI (RFC 9111), but that only covers the server-to-cache path. For your own application, you need a strategy. One approach is to use versioned keys: instead of product:123, use product:123:v2 when the price changes. That way, old entries expire naturally, and you don't need to purge anything. Another is to use Redis pub/sub to broadcast invalidation messages to all nodes (Redis). But the blunt truth is: cache invalidation is hard, and there's no one-size-fits-all. The key is to design your cache keys and TTLs to minimize the blast radius of invalidation.
Myth: You Should Cache Everything at the Edge
Just because you can cache something at the edge doesn't mean you should. Edge caches are great for public, static content, but they're terrible for personalized data. If you cache a user's dashboard at the edge, you'll either serve someone else's data or you'll need to include the user ID in the cache key, which defeats the purpose. Even worse, if you cache a response that should be private, like a banking statement, you could leak data. The Cache-Control: private directive tells shared caches not to store a response (MDN). So, use edge caches for what they're good at: static assets, public pages, and API responses that are identical for all users. For dynamic, personalized content, cache at the application layer or in a distributed cache like Redis, where you can control access and invalidate precisely.
Quick Tip
Before you add a cache, measure. Know your hit ratio and your origin load. A cache that isn't measured is a cache you're guessing about.
What I'd Actually Do
Here's my blunt recommendation: start with a layered approach, but don't over-engineer. Use a CDN for static assets with a long TTL (like images, CSS, JS) and set Cache-Control: public, max-age=31536000, immutable for fingerprinted assets. For dynamic pages, use a short TTL (like 60 seconds) plus stale-while-revalidate to smooth out spikes. Use Redis as your application cache with allkeys-lru eviction, and set TTLs based on how stale your data can be—don't default to no TTL. For invalidation, use explicit purges (like Cloudflare's single-file purge) or versioned keys. And don't forget the load balancer: if you're using nginx, turn on caching there too, and use consistent hashing if you have a stateful backend. Finally, monitor your hit ratios and adjust. Caching is a continuous tuning exercise, not a one-time setup.
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!