skip to content

Reviewing a client integration, you notice every API call appends a unique query parameter such as ?_=1739382736 or ?nocache=<uuid>. What effect does that have on caches, and when is a changing URL the right tool rather than a mistake?

level: middleimportance: should knowfreq 36%

answer

  1. cache key = method + full URI (+ Vary headers)
  2. unique param → unique key → 0% hits + eviction churn
  3. jQuery cache:false legacy timestamp
  4. content-addressed URL = cache forever, publish new URL
  5. canonicalize params; drop tracking junk

basics

~20 s

A unique parameter makes a unique cache key, so nothing is ever reused: hit rate is zero and every request reaches the origin. A changing URL is right only in the inverse case — content-addressed or versioned URLs that change when the content changes, allowing very long lifetimes.

solid answer

~50 s

Caches key on method plus the full URI, query string included. A per-request unique parameter therefore mints a fresh key every time: the browser cache, any proxy and the CDN all miss, every request hits the origin, and the cache fills with single-use entries that evict useful ones. It is a blunt instrument — usually cargo-culted from old "disable caching" workarounds — and the correct fix is to declare the resource's freshness policy on the response, or revalidate with a conditional request, rather than to lie about the identity of the resource. The inverse pattern is legitimate and powerful: **change the URL when the content changes**. Fingerprinted asset URLs and versioned resource identifiers make the URL content-addressed, so the response can be cached effectively forever and a deploy simply references a new URL. That is invalidation by naming rather than by purging. Related key-fragmenting mistakes: leaving tracking parameters in API URLs, inconsistent parameter order or casing, and session identifiers in the path.

go deeper

for a junior

Know that the cache key includes the query string, so a unique parameter means nothing is ever a cache hit.

for a middle

Add the knock-on effects — no 304s, cache pollution, metric cardinality — and contrast with fingerprinted URLs that change only when content changes.

for a senior

Diagnose why the buster was added, fix the freshness declaration at the source, and address key fragmentation from parameter order and unbounded filters.

for a principal

Set URL-design policy: immutable content gets content-addressed URLs and long lifetimes, mutable content gets short lifetimes and validators, and nothing lives in the dangerous middle.

## Why the parameter matters An HTTP cache's key is, at minimum, the request method plus the effective request URI, including the entire query string, plus whatever additional request headers the response's `Vary` names. Two URLs that differ by a single character are two unrelated resources as far as every cache in the path is concerned. So `?_=1739382736`, where the value is the current millisecond, guarantees a distinct key per request. The consequences cascade: - **Zero reuse.** Browser cache, corporate proxy, CDN edge: all miss, always. Every single request is served by the origin. - **Cache pollution.** Each single-use entry occupies storage and, in an LRU edge, evicts entries that would have been reused. The endpoint degrades neighbouring endpoints' hit rates. - **Conditional requests become pointless.** A validator is attached to a URL that will never be requested again, so `If-None-Match` never matches and 304s never happen. - **Observability noise.** Origin request counts and the cardinality of URL dimensions in metrics and logs explode; per-URL dashboards become useless. Where does it come from? Historically, from clients working around badly-behaved caches and old browsers that ignored freshness rules — jQuery's `cache: false` literally appended `_=<timestamp>`. It solved a real problem in a world where servers did not label their responses. Today the honest fix lives on the response: state the freshness policy explicitly, or require revalidation, which lets the cache participate correctly instead of being blinded. ## The legitimate inverse: URLs that change with content The same mechanism, used deliberately, is the strongest caching pattern available. If the URL is derived from the content — `/assets/app.9c1f7b2e.js`, or a versioned resource `/v1/config/rev/8817` — then: - the response can be given an extremely long freshness lifetime, because the bytes at that URL will never change; - publishing new content means publishing a **new URL**, and every cache in the world is instantly correct without a purge; - rollbacks are trivial: point at the old URL again, which is still cached. The discipline is: a URL either names something immutable and is cached forever, or names something mutable and carries a short lifetime plus a validator. The failure mode is the middle ground — mutable content behind a long lifetime, which is what forces emergency purges. For APIs, the practical application is a small mutable "pointer" resource with a short lifetime that names a large immutable resource with a long one. Clients poll the cheap pointer; the expensive payload is fetched once per version and reused by everyone. ## Other ways URL design defeats the cache - **Tracking and correlation parameters.** `?utm_source=...`, `?requestId=...` on API calls fragment keys for no benefit. Strip or normalize them at the edge, or keep them out of API URLs entirely. - **Inconsistent parameter order, casing or encoding.** `?a=1&b=2` and `?b=2&a=1` are different keys unless the edge canonicalizes. Two clients that build query strings differently silently halve the hit rate. - **Unbounded filter and sort combinations.** Free-form filtering explodes key cardinality, so each key is requested once and nothing stays warm. Constraining the supported combinations is a cache decision as much as a database one. - **Session identifiers in the path.** `/s/8f3a92/products` gives every session its own copy of shared content — a total loss of shared caching, plus a security problem, since URLs leak into logs and referrers. - **Unstable pagination cursors.** A cursor encoding a current timestamp changes the URL on every pass through the same data. ## What to do in the review Ask what the parameter is protecting against. If the answer is "we were seeing stale data", the real defect is a missing or wrong freshness declaration on the response, or a shared cache storing something it should not — both fixable on the server, where the knowledge lives. Removing the buster then converts a permanent 0% hit rate into whatever the resource's real reuse potential is, usually the single largest caching win available on a mature API.

  • A team added a cache-busting parameter because clients were seeing stale data. What is the correct fix?
    Fix the labelling on the response instead of disguising the request. Give the resource an explicit, honest freshness lifetime, add a validator so clients can revalidate cheaply, and check whether a shared cache is storing something it should not. That restores reuse for everything that is genuinely reusable, which the buster destroys unconditionally.
  • How do fingerprinted URLs make invalidation unnecessary?
    Because the URL is derived from the content, new content necessarily has a new URL, and no cached entry ever becomes wrong — it merely becomes unreferenced and ages out. Invalidation becomes a naming problem solved at publish time rather than a distributed purge executed under pressure.

Cache-busting is renaming a book before every visit so the library can never say 'we already have that one'. Content-addressed URLs are the opposite: a new edition gets a new catalogue number, so the old copy stays valid forever and nobody has to hunt the shelves to remove it.

saying these in an interview costs you the question

  • Believing a cache-busting parameter only affects the browser cache
  • Thinking caches ignore query strings when computing the key
  • Treating cache-busting as equivalent to declaring a no-store policy on the response
  • Confusing per-request random URLs with content-addressed versioned URLs
  • Leaving tracking parameters on API URLs and assuming the CDN normalizes them

context