CDN caching rules for dynamic sites that still feel instant
Logged-in users, carts and personalization make caching feel impossible. How we design CDN rules that cache most traffic without serving anyone the wrong page.

Many of the sites we audit have a CDN in front of them that caches almost nothing. Static assets are cached, the HTML is not, and every page view still travels all the way to the origin server. The reason is usually defensive: someone once served a logged-in page to an anonymous visitor, or a cart showed another customer's items, and the fix was to disable HTML caching entirely. That removes the risk and most of the benefit. With careful rules, even stores and membership sites can serve the majority of page views from the edge.
Why HTML caching matters
Static asset caching is table stakes and already handled by most setups. The big wins for real users come from caching the HTML document itself, because it is the first thing the browser needs and everything else waits for it. Serving HTML from an edge location close to the visitor rather than from an origin on another continent routinely cuts time to first byte from 600 to 900 milliseconds down to under 100. That improvement flows directly into Largest Contentful Paint and every metric that depends on it.
There is a cost benefit too. When a DTC client moved from asset-only caching to full-page caching for anonymous visitors, origin requests dropped by 78 percent, and they were able to run their application on half the servers during a seasonal sale.
Designing the cache key
The cache key determines which requests are considered the same. Get it wrong in one direction and you serve the wrong content; get it wrong in the other and your hit rate collapses.
- Strip tracking parameters. Marketing parameters such as campaign tags and click identifiers make every link from an ad unique. Remove them from the cache key; the page content does not depend on them.
- Keep parameters that change content. Pagination, filters and sort orders on category pages must stay in the key.
- Vary only on what you must. If you serve different HTML by country or currency, add that as a key component based on a header or cookie the CDN sets, not on the full set of request headers.
- Normalize. Lowercase paths where your application treats them as equivalent, sort query parameters and drop trailing slashes consistently.
Handling cookies and logged-in users
The core rule is simple: if a request carries a session or authentication cookie, bypass the cache; otherwise, ignore cookies entirely and serve from cache. The failure mode to avoid is the reverse situation, where the origin sets a session cookie on the very first anonymous visit. Many frameworks and plugins do this by default, and it silently makes every visitor uncacheable after their first page.
if (req.cookies.has("session_id") || req.cookies.has("logged_in")) {
return fetch(req, { cache: "bypass" });
}
const key = normalizeUrl(req.url, { stripParams: ["utm_*", "gclid", "fbclid"] });
return cachedFetch(key, { ttl: 300, staleWhileRevalidate: 86400 });
Audit which cookies your application actually sets for anonymous visitors, and delay creating a session until the visitor does something that needs one, such as adding to cart or signing in.
Most personalization problems are really session problems. Stop creating sessions for visitors who have not asked for anything.
Carts, prices and other dynamic fragments
A cached product page still needs to show the right cart count and, for some stores, stock levels or member prices. Rather than giving up on caching the page, split the dynamic parts out:
- Cache the page shell and product content for everyone.
- Load cart count, account menu and recently viewed items with a small request to an uncached endpoint after the page renders.
- For stock and price, either accept short staleness with a low TTL and purge on change, or fetch them with the same small request when accuracy is essential.
The fragment request is tiny and fast, and the visitor sees a complete page within a few hundred milliseconds either way. The layout must reserve space for these fragments so they do not cause layout shift when they arrive.
Freshness: TTLs, stale-while-revalidate and purging
Long TTLs maximize hit rates but risk stale content. Short TTLs keep content fresh but send more traffic to the origin. Two techniques make the trade-off much easier.
Stale-while-revalidate lets the CDN serve a slightly old copy instantly while fetching a fresh one in the background. Visitors never wait for the origin, and content updates within seconds of expiry. We typically pair a short TTL of a few minutes with a stale window of a day, which also keeps the site up if the origin briefly fails.
Tag-based purging lets the application tell the CDN exactly what to invalidate when content changes. When a product is edited, the application purges the tag for that product, which clears its page, its category listings and any other page that displays it. This is far more precise than purging by URL and far safer than purging everything.
Avoid full-cache purges as a routine operation. On a busy site, purging everything sends a thundering herd to the origin, and we have seen more than one outage start that way immediately after a deploy.
Measuring the result
Track the cache hit ratio for HTML separately from assets; the combined number hides what matters. A reasonable target for a content site is above 90 percent on HTML, and for a store with logged-in customers, somewhere between 60 and 85 percent. Pair that with real-user measurements of time to first byte by country, so you can see the improvement where visitors actually are. Our CDN configuration work covers the rules, purging integration and monitoring, and we usually measure the before and after as part of broader Core Web Vitals optimization.
Make your dynamic site feel static
If your CDN is mostly serving images while your origin does all the work, there is usually a lot of speed left on the table. Tell us about your platform and traffic, and we will send a fixed-price proposal within 24 hours. Start here.



