Query parameters significantly impact how URLs are cached. Understanding caching behavior helps you design cache-friendly URLs and avoid performance pitfalls.
Key Takeaways
- 1Each unique query string is typically cached separately
- 2Random or timestamp parameters bust the cache entirely
- 3Normalize parameter order for consistent cache keys
- 4CDNs can be configured to ignore certain parameters
- 5Static assets should use versioned paths, not query strings
“The primary cache key consists of the request method and target URI... When presented with a request, a cache MUST NOT reuse a stored response unless the presented effective request URI and that of the stored response match.”— RFC 7234, Section 4
How URL Caching Works
Caches—whether in browsers, CDNs, or proxies—use the complete URL as a lookup key. This simple rule has important implications: any change to the query string, no matter how trivial, creates a separate cache entry.
| URL | Cache Key | Cached Separately? |
|---|---|---|
| /page | /page | Baseline |
| /page?v=1 | /page?v=1 | Yes, different key |
| /page?v=2 | /page?v=2 | Yes, different key |
| /page?a=1&b=2 | /page?a=1&b=2 | Yes |
| /page?b=2&a=1 | /page?b=2&a=1 | Yes! Order matters |
Notice the last two rows: ?a=1&b=2 and ?b=2&a=1 are cached separately even though they represent the same parameters. This is a common source of cache inefficiency that's easy to overlook.
Sometimes you want to bypass the cache—for example, when deploying new versions of static assets.
Cache Busting
Cache busting deliberately changes the URL to force a fresh fetch. This is essential when you deploy updated JavaScript, CSS, or images that users might have cached indefinitely.
https://example.com/app.js?v=1.2.3// Common cache-busting patterns
// 1. Version number
'/app.js?v=1.2.3'
// 2. Timestamp (forces refresh every time - use sparingly!)
`/app.js?t=${Date.now()}`
// 3. Content hash (best practice)
'/app.a1b2c3d4.js' // Better: hash in filename
// 4. Build ID
`/app.js?build=${process.env.BUILD_ID}`While version and build parameters work, content hashing in the filename (option 3) is the gold standard. It changes only when the file content changes, giving you perfect cache invalidation without manual version management. Most bundlers like Webpack and Vite generate these hashes automatically.
When cache busting causes problems—like tracking parameters fragmenting your cache—CDN configuration can help.
CDN Configuration
CDNs let you customize how query parameters affect caching. You can ignore certain parameters, normalize ordering, or strip tracking codes—all at the edge, before requests hit your origin server.
# Cloudflare: Cache Rules can ignore query strings
# "Cache by query string" options:
# - All: Full URL is cache key
# - Ignore: Query string excluded from cache key
# - Include specified: Only listed params affect cache
# - Exclude specified: Listed params ignored
# Fastly/Varnish example
sub vcl_hash {
# Normalize parameter order
set req.url = querystring.sort(req.url);
# Remove tracking parameters from cache key
set req.url = querystring.filter(req.url,
"utm_source" + "utm_medium" + "utm_campaign" + "fbclid");
}The Fastly/Varnish example shows two powerful techniques: sorting parameters for consistency and stripping tracking parameters that don't affect content. This dramatically improves cache hit rates for marketing-heavy sites where every URL comes decorated with UTM codes.
Even with good configuration, caching problems creep in. Here are the most common issues and their fixes.
Common Caching Problems
These problems often go unnoticed until you check your cache hit ratio or notice unexpectedly high origin load. Regular auditing of your cache keys helps catch these issues early.
| Problem | Cause | Solution |
|---|---|---|
| Low cache hit rate | Tracking params (utm, fbclid) | Strip at CDN edge |
| Cache pollution | Random timestamps | Use content hashing instead |
| Duplicate cache entries | Different parameter order | Normalize URL order |
| Stale data | Insufficient cache busting | Version static assets properly |
The first three problems hurt performance; the fourth hurts users who see outdated content. A good caching strategy addresses all four by separating concerns: let content-affecting parameters vary the cache key, strip everything else, and version assets properly.
Best Practices
Follow these guidelines to get the best cache performance while ensuring users always see fresh content when it matters.
// 1. For static assets: use content hash in filename
// ❌ /app.js?v=123
// ✅ /app.a1b2c3d4.js
// 2. For API responses: use proper Cache-Control headers
// Headers matter more than URL structure
fetch('/api/data', {
headers: { 'Cache-Control': 'max-age=3600' }
});
// 3. Separate cacheable and non-cacheable params
// Analytics params shouldn't affect caching
// /page?category=shoes <- affects content
// /page?category=shoes&utm_source=google <- utm shouldn't bust cache
// 4. Normalize URLs before caching
function normalizeForCache(url) {
const u = new URL(url);
// Remove tracking params
['utm_source', 'utm_medium', 'utm_campaign', 'fbclid', 'gclid']
.forEach(p => u.searchParams.delete(p));
// Sort remaining params
const sorted = [...u.searchParams.entries()].sort();
u.search = new URLSearchParams(sorted).toString();
return u.href;
}The normalizeForCache function demonstrates a complete implementation: it removes tracking parameters that don't affect page content and sorts the remaining parameters for consistency. Apply this logic at your CDN edge for maximum benefit, or in your application layer if CDN configuration isn't available.