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.css as app.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/about and /en-us/about are 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=yyy doesn'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 lang or page) 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: private or 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