Open Calculator

CDN Cache-Hit Ratio Engineering: Cache-Control Directives, Query String Normalization, and Vary Headers

Published April 07, 2026 · Edge Caching & RFC 9111 Specification

A Content Delivery Network's Byte Cache-Hit Ratio (Byte CHR) measures the percentage of outbound bytes served directly from edge RAM/NVMe storage versus bytes fetched from the origin server (`Edge_Hit_Bytes / Total_Delivered_Bytes`). Raising your Byte CHR from 80% to 96% does not merely improve edge efficiency by 16 percentage points—it reduces origin bandwidth egress and origin I/O load by 80% (dropping origin miss exposure from 20% down to 4%).

Furthermore, infrastructure teams must distinguish between Request CHR and Byte CHR. A CDN dashboard may report a healthy `94% Request Cache-Hit Ratio` because millions of tiny 2 KB CSS/SVG icons are cached, while 50 MB software installers or video segments suffer a `45% Byte Cache-Hit Ratio` due to unconfigured byte-range slicing or LRU cache eviction—causing massive origin egress bills despite a green Request CHR metric.

Origin Egress Compression by Byte Cache-Hit Ratio

Assuming a production workload delivering 200 TB/month from an origin cloud charging $0.085/GB for origin cache-miss egress:

Byte Cache-Hit Ratio Origin Miss Ratio Billed Origin Egress (TB) Origin Egress Cost ($0.085/GB)
65.0% (Unnormalized Keys) 35.0% 70.0 TB $5,950.00 / mo
85.0% (Default Edge TTL) 15.0% 30.0 TB $2,550.00 / mo
95.0% (Normalized + s-maxage) 5.0% 10.0 TB $850.00 / mo
98.5% (Immutable + Origin Shield) 1.5% 3.0 TB $255.00 / mo

Separating Browser TTL (`max-age`) from Shared Edge TTL (`s-maxage`)

Engineers frequently set short `max-age` values (e.g., `Cache-Control: public, max-age=300`) because they fear end-user browsers will hold stale files after a deployment. However, under RFC 9111 (HTTP Caching), setting `max-age=300` without `s-maxage` forces every CDN edge node globally to evict or revalidate the object every 5 minutes—resulting in hundreds of origin cache misses per hour across long-tail PoPs.

Instead, decouple browser caching from shared CDN caching using `s-maxage` (Shared Max-Age) paired with `stale-while-revalidate` and `stale-if-error` (RFC 5861). This instructs the CDN edge to hold the asset for days or weeks (relying on instant API cache-tag purge on deploy) while end-user browsers check the CDN edge every 60 seconds:

# 1. Content-Addressed Immutable Assets (Webpack/Vite hashed JS, CSS, WOFF2, Fingerprinted Media)
Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable
# 2. Dynamic Public JSON Catalogs, Avatars, or Unfingerprinted Media
Cache-Control: public, max-age=60, s-maxage=86400, stale-while-revalidate=3600, stale-if-error=86400
# 3. CDN-Specific Surrogate Header (Overrides browser Cache-Control strictly on the CDN Edge)
CDN-Cache-Control: public, max-age=604800, stale-while-revalidate=86400

Eliminating Query-String Cache Poisoning (`utm_*`, `fbclid`, `gclid`)

By default, CDNs construct the internal lookup hash key using the full request URI including query parameters (`Scheme + Host + Path + QueryString`). When ad platforms or analytics scripts append unique click identifiers (`?fbclid=IwAR1...`, `?gclid=Cj0KC...`, `?utm_source=newsletter&_t=1758291`), every visitor generates a unique cache key—dropping your CDN cache-hit ratio toward **0%** and routing 100% of traffic to your origin server.

  • Static Asset Prefixes (`/assets/*`, `/media/*`, `/downloads/*`): Configure your CDN Cache Key Policy to Ignore All Query Strings so `/video/intro.mp4?fbclid=123` and `/video/intro.mp4?utm_source=x` resolve to the single canonical cache key `/video/intro.mp4`.
  • Dynamic Image Resizers & APIs: Switch from a blocklist to an explicit **Query String Allowlist** (permitting only functional keys such as `w`, `h`, `q`, `fmt`, `page`) and enable **Alphabetical Query Parameter Sorting** at the edge so `?fmt=webp&w=800` and `?w=800&fmt=webp` share a single cache entry.

Auditing `Vary`, `Set-Cookie`, and Byte-Range Slicing

Three HTTP protocol mechanisms silently degrade shared edge caching across CloudFront, Cloudflare, Bunny.net, and Fastly:

  1. `Vary: User-Agent` or Unnormalized `Vary: Accept`: Instructs the CDN to store a separate cached copy for every distinct client header string. Because there are thousands of mobile and desktop `User-Agent` permutations, cache efficiency collapses. Only permit `Vary: Accept-Encoding` (which CDNs normalize internally into `br`, `gzip`, or `identity`).
  2. Accidental `Set-Cookie` Headers on Static Responses: If an origin load balancer (`AWS ALB`) or session middleware attaches a `Set-Cookie` header to image or binary responses, RFC-compliant CDNs either bypass caching entirely (`Cf-Cache-Status: BYPASS`) or strip the cookie. Strip incoming `Cookie` request headers at the CDN edge for static path prefixes before forwarding misses to the origin.
  3. Large-File Byte-Range Slice Caching (`HTTP 206 Partial Content`): When delivering files larger than 20 MB (`.mp4` videos, `.zip` archives, `.safetensors` weights), enable **CDN Slice / Range Caching** (4 MB or 8 MB chunk slicing). This allows the CDN to cache and evict discrete 4 MB byte ranges independently rather than aborting or re-fetching a 2 GB file when a client seeks mid-stream.

Diagnostic Verification via `curl` & Field Post-Mortem

Before deploying a new asset pipeline into production, verify that your CDN edge returns a `HIT` even when random marketing query parameters and cookies are injected by executing two sequential `curl -I` head requests:

# 1. Prime the Edge PoP Cache
curl -sI "https://cdn.daktan.com/media/hero-banner.webp" | grep -iE "cache|age|etag|vary"

# 2. Test Query-String & Cookie Immunity (Must return HIT and Age > 0)
curl -sI -H "Cookie: session_id=test_99182" \
  "https://cdn.daktan.com/media/hero-banner.webp?utm_source=adwords&fbclid=IwAR99281" \
  | grep -iE "cf-cache-status|x-cache|age|cache-control|vary"

# Expected Healthy Output:
# cache-control: public, max-age=604800, s-maxage=2592000
# cf-cache-status: HIT (or x-cache: Hit from cloudfront)
# age: 42
# vary: Accept-Encoding
Field Post-Mortem: Why a 93% Request CHR Hid a 41% Byte CHR

During a Q1 audit of a SaaS documentation portal serving `68 TB/mo`, the team's CDN dashboard showed a `93.4%` overall cache-hit rate, yet origin egress from `eu-central-1` remained above `40 TB/mo`. Inspecting raw access logs revealed that 180 MB desktop `.dmg` and `.exe` installers were served from an origin route that attached a `Set-Cookie: _locale=en` header on first visit. While 4 KB static icons cached at `99%`, every 180 MB installer download returned `Cf-Cache-Status: BYPASS`. Stripping `Set-Cookie` on `/releases/*` dropped origin egress from `40.1 TB/mo` to `1.9 TB/mo` within 24 hours.

Collapse Regional Cache Misses with Origin Shield

Read our next architectural guide on deploying a mid-tier Origin Shield to eliminate multi-PoP cache misses.

Origin Shield Guide →