CDN Cache Keys: The Hidden Switch That Decides Your Hit Rate
The cache key is what a CDN uses to decide whether a request can be served from cache. Plenty of people set cache durations but never touch the cache key — the result is either a hit rate that won't climb or different users seeing the same page with the wrong content. The cache key matters because it decides how many copies of the same content get stored and how many requests can reuse each one. This guide breaks down what a cache key is made of and how to configure it so you get higher hit rates without mixing up content.
What a Cache Key Is Made Of
A concrete example first: https://example.com/product/42?lang=zh&utm_source=weibo and https://example.com/product/42?lang=zh&utm_source=baidu serve identical page content — only the tracking parameter differs. If the cache key includes the query string, those two requests each get their own cached copy, wasting space and lowering the hit rate; if the key is just path + lang, they share one cache entry. A cache key is usually a combination of several dimensions:
| Dimension | Include? | Why |
|---|---|---|
| URL path | ✅ Always | Different pages differ |
| Host | ✅ Always | Prevents cross-site mixing on multi-domain setups |
| Query params | ⚠️ Allowlist | Keep only the ones that change content |
| Cookies / headers | ⚠️ Careful | Only for authenticated endpoints |
| Language | ✅ As needed | Mandatory for multilingual sites |
Design Principles
- Static assets: strong caching keyed by file hash: publish
app.cssasapp.a1b2c3.css— when content changes, the filename changes and the cache key naturally invalidates, so you can safely set a one-year lifetime; - HTML pages: distinguish by path + language:
/zh-cn/aboutand/en-us/aboutare different content; the cache key must include the language prefix, or English users will see Chinese pages; - Whitelist marketing parameters:
?utm_source=xxx&utm_campaign=yyydoesn't change page content, but including it splits one page into countless cache copies and tanks the hit rate; - Never cache login state or cart APIs: these are per-user; caching them risks mixed data at best and leaked privacy at worst.
How to Monitor the Cache Hit Rate
Whether your cache key is right shows up in the hit-rate data. Every major CDN has hit-rate reporting: on Cloudflare, check the Cache Hit Ratio in the Caching panel; on Alibaba Cloud CDN, watch the hit-rate trend under monitoring. The bar is simple — static assets should sit above 90%; dynamic pages can be lower, but don't let them stay under 40% for long. If the hit rate suddenly drops, check whether you changed the cache key, added a parameter, or accidentally altered Cache-Control on the origin responses; cross-reference the change against your release log by timestamp and the culprit usually surfaces quickly.
Cache Invalidation: Getting Old Content Off the Edge After a Release
The cache key decides what hits; invalidation decides how fast updates take effect. After a redesign, the worst case is old pages lingering on edge nodes: either you wait for TTLs to expire or you purge them. Two practical tools: version your assets (change the filename when you change JS/CSS, and new pages naturally reference the new files), and use the CDN's purge feature to clear the relevant URLs after each release. Don't do a full flush during peak hours — that instantly saturates origin bandwidth and creates a new incident. Get into the habit of "purge on release, purge by scope" — it's far more reliable than waiting on TTL expiry.
Config Examples: Cloudflare and Nginx
In Cloudflare, use Cache Rules to control the cache key:
{
"expression": "(http.host eq \"example.com\")",
"action": {
"type": "set_cache_settings",
"cache_key": {
"include_query": false,
"include": ["header:accept-language"]
}
}
}
In Nginx, use proxy_cache_key to customize the key:
location / {
proxy_cache_key "$host$request_uri";
# Static assets keyed by file hash
location ~* \.(css|js|png|jpg)$ {
proxy_cache_key "$host$uri";
expires 1y;
}
}
The idea is the same in both: for dynamic pages, drop the query string from the key; for static assets, cache by URI for a year.
A Real Scenario: A Multilingual E-Commerce Site
Say you run a Chinese/English e-commerce site and product pages carry parameters like ?lang=zh&utm_source=weibo. If the cache key includes every query parameter, then ?lang=zh&utm_source=weibo, ?lang=zh&utm_source=baidu, and ?lang=en each get their own cached copy — one product split into dozens of cache entries. The right approach: include lang in the cache key and exclude utm_*. After the change, the hit rate can jump from 40% to over 90%, origin-fetch pressure drops dramatically, and pages load faster too. Here's a typical before/after comparison:
| Config | Cached Copies | Hit Rate | Origin Pressure |
|---|---|---|---|
| All query params in key | Dozens per product | ~40% | High |
Only lang in key |
2-3 per product | 90%+ | Much lower |
Common Problems
- All query parameters in the cache key lower the hit rate: fix it with an allowlist — keep only params that genuinely affect content (like
langorpage) and drop the rest; - Multilingual pages not separated by language cause mixed content: add language explicitly to the cache key, or put a language prefix in the URL path;
- Users seeing someone else's data after login: keep authenticated pages off the public cache with
Cache-Control: privateor by bypassing the CDN entirely; - Origin content updated but pages haven't changed: the edge is still serving an old copy within its TTL. First check whether an unchanged cache key is hitting the old copy, then decide between waiting for expiry or purging.
References: Cloudflare Cache Keys docs https://developers.cloudflare.com/cache/how-to/cache-keys/ , Nginx proxy_cache_key docs https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_key