A CDN makes your static assets fly. Images, CSS, JavaScript, fonts, all served from a node close to your visitor instead of your origin server halfway around the world. But then someone adds a shopping cart, a login form, or a personalized dashboard, and suddenly the team is scared to touch the CDN configuration at all. Cache everything and users see stale prices. Cache nothing and you lose most of the benefit of a CDN for website speed.
The good news: you don't have to choose between speed and freshness. You just need caching rules that understand the difference between content that never changes and content that changes for every single visitor.
Why Dynamic Content Breaks Naive CDN Setups
Most CDNs default to caching based on the URL and sometimes the request method. That works fine for /images/logo.png. It works terribly for /api/cart or /account/orders, where the response depends on who's asking, what's in their session, or what just happened five seconds ago.
If your CDN caches a personalized page and serves it to the next visitor, that's not just a bug. It's a privacy and trust problem. This is why so many teams either disable caching entirely on dynamic routes or build convoluted URL-based exclusion lists that break the moment someone adds a new page.
The Real Goal: Cache Selectively, Not Globally
The fix isn't an all-or-nothing switch. It's teaching your CDN which parts of a response are safe to cache and for how long, using signals your application already knows.
Use Cache-Control Headers as Your Primary Tool
Every response your server sends can carry a Cache-Control header, and this should be the source of truth your CDN respects, not an override you fight against.
- Static assets: Cache-Control: public, max-age=31536000, immutable for files with hashed filenames like app.a1b2c3.js. Since the filename changes when the content changes, you can cache these for a year without risk.
- Semi-dynamic pages: Cache-Control: public, max-age=60, stale-while-revalidate=300 for a blog homepage or category listing that changes occasionally but doesn't need to be instant.
- Truly personalized content: Cache-Control: private, no-store for account pages, carts, and anything tied to a session cookie.
The stale-while-revalidate directive deserves special attention. It tells the CDN to serve the cached version immediately while fetching a fresh copy in the background. Your visitor never waits for the update, and the next visitor gets the new version. This single header eliminates most of the tradeoff between speed and freshness for content that updates every few minutes rather than every few seconds.
Vary Your Cache by the Right Keys
Sometimes a page is mostly static but differs slightly by cookie, language, or device. Instead of marking the whole thing uncacheable, use the Vary header to tell the CDN to store separate cached copies per variation.
Vary: Accept-Language, CookieBe careful here. Varying on Cookie without narrowing it down can explode your cache into thousands of near-duplicate entries, one per unique session, which defeats the purpose. Most CDNs let you specify which cookie names actually matter, so only vary on the ones that change the response, like a currency or locale preference, not analytics tracking cookies.
Split Pages Into Cacheable and Uncacheable Fragments
A product page might be 95% identical for every visitor, with only a "Welcome back, Sarah" banner and a cart count changing. Rather than making the whole page uncacheable, many teams cache the page shell at the edge and load the personalized bits via a small client-side JavaScript call to an uncached API endpoint. This pattern, sometimes called edge-side includes or client-side hydration, lets a CDN for website speed do its job on 95% of the page weight while only the tiny personalized fragment hits your origin.
Set Sensible TTLs Based on How Fast Content Actually Changes
A common mistake is picking one TTL for the whole site. Instead, match the cache duration to how often each content type realistically changes:
- Product images and CSS: cache for a year, use versioned filenames
- Blog posts and marketing pages: cache for 5-15 minutes with stale-while-revalidate
- Category and search result pages: cache for 60-120 seconds
- Pricing and inventory data: cache for 10-30 seconds, or serve uncached with a fast origin
- Cart, checkout, account pages: never cache
These numbers aren't universal rules, they're a starting point. Watch your CDN's analytics for cache hit ratio per route. A hit ratio above 80% on cacheable content usually means your TTLs are working well. If a route sits below 50%, either the TTL is too short or the route shouldn't be cached at the edge at all.
Purge Instead of Waiting
Short TTLs are a safety net, not a strategy. If you publish a blog post or update a price, you shouldn't have to wait five minutes for the cache to catch up. Most CDNs support programmatic purging, either by URL, by tag, or by a wildcard pattern. Wiring this into your publishing workflow, so a "publish" click also fires a purge request, means you can set longer TTLs everywhere without sacrificing freshness when it actually matters.
Tag-Based Purging Beats URL-Based Purging
If your CDN supports cache tags, use them. Instead of tracking every URL that includes a piece of content (a product that appears on its own page, a category page, and a homepage carousel), you tag all three responses with product-1234 and purge that single tag when the product updates. It's a small setup cost that saves a lot of debugging later.
Don't Forget the Origin Side of the Equation
None of this matters if your origin server itself is slow when the CDN does need to check in. Object caching on your server, using something like Redis to store database query results in memory, keeps that background revalidation fast even on personalized or semi-dynamic routes. We run this kind of caching automatically for WordPress sites on our platform, with a dashboard showing real hit rates, so the origin stays quick even when the CDN can't help. For a deeper look at what happens between the CDN and your server, see how a CDN changes the geography of your website's performance.
It's also worth understanding why your time to first byte affects conversions, since every cache miss ultimately depends on how fast your origin responds. If you're picking a CDN in the first place, our guide on choosing a CDN edge network that covers your audience is a good next stop.
The Takeaway
Freshness and speed aren't opposites, they're both solved by being specific. Cache aggressively where content is truly static, use short TTLs with stale-while-revalidate where content changes gradually, and never cache anything tied to a session or a cart. Set up purging so you don't have to rely on TTLs alone for the moments that matter. Get these caching rules right, and a CDN for website speed stops being a risky black box and starts being one of the most reliable performance wins you can make.
For server-side caching that pairs well with this setup, see our overview of server-level caching or check out how Redis caching works on managed hosting.