← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Asset Versioning & Browser Cache Strategy

performance Beginner

Also Known As

browser caching static asset cache cache busting

TL;DR

Serving static assets with immutable long-lived cache headers plus content-hash filenames — maximising cache hits while guaranteeing instant cache-busting on change.

Explanation

The optimal static asset strategy: append a content hash to filenames (app.a3f8d2.js) and serve with Cache-Control: public, max-age=31536000, immutable. Browsers cache indefinitely; on next deploy the hash changes so the new filename is fetched fresh — no stale asset problem. The HTML document itself gets Cache-Control: no-cache, must-revalidate so it always reflects the latest asset filenames. Build tools (Vite, Webpack, Laravel Mix) handle hash generation and manifest files that PHP uses to resolve hashed filenames. For PHP-served dynamic content: use ETag or Last-Modified headers and respond with 304 Not Modified to save bandwidth. Avoid query-string versioning (?v=123) — some CDNs and proxies strip or ignore query strings when caching.

Diagram

flowchart LR
    subgraph No Content Hash
        BROWSER[Browser] -->|GET app.css| SERVER[Server]
        SERVER -->|200 app.css v1| BROWSER
        DEPLOY[New deploy v2] --> SERVER
        BROWSER -->|cached - serves v1!| OLD[Stale CSS]
    end
    subgraph Content Hash in Filename
        B2[Browser] -->|GET app.a3f2c.css| S2[Server]
        S2 -->|200 Cache-Control: max-age=31536000| B2
        DEPLOY2[New deploy] --> NEW[app.b7d9e.css - new hash]
        B2 -->|new filename - fetches fresh| FRESH[Always fresh]
    end
    style OLD fill:#f85149,color:#fff
    style FRESH fill:#238636,color:#fff

Common Misconception

Setting a long Cache-Control max-age means users always get stale assets after a deployment. Cache busting via content-hashed filenames (app.a1b2c3.js) lets you set immutable long-lived cache headers while guaranteeing users receive updated files immediately after deployment.

Why It Matters

Long cache lifetimes for static assets eliminate repeated downloads — cache-busting via content hashes ensures users get updated files immediately without waiting for cache expiry.

Common Mistakes

  • Using a query string for cache-busting (?v=2) — some proxies ignore query strings and serve stale content.
  • Short Cache-Control max-age on static assets — defeats the performance benefit.
  • Not using immutable directive for fingerprinted assets — browsers may still revalidate without it.
  • Cache-Control: no-cache on static assets in production — every request hits the server.

Code Examples

✗ Vulnerable
// Version query string — proxy-unfriendly cache busting:
<script src="/app.js?v=1.2"></script>

// Content-hash filename — correct approach:
<script src="/app.abc123f.js"></script>
<!-- Server: Cache-Control: public, max-age=31536000, immutable -->
✓ Fixed
// Content-hash filenames + immutable cache headers
// Build tool (Vite) output: app.a3f8d2b1.js

// PHP: read Vite manifest to resolve hashed filenames
$manifest = json_decode(file_get_contents(public_path('build/manifest.json')), true);
$assetUrl = '/build/' . $manifest['resources/js/app.js']['file'];
// → /build/assets/app-a3f8d2b1.js

// Serve with immutable header (nginx)
location ~* \.(js|css|woff2|png|webp)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
    add_header Vary Accept-Encoding;
}

// HTML document: no-cache so browsers always check for new asset filenames
add_header Cache-Control "no-cache, must-revalidate";

Added 15 Mar 2026
Edited 22 Mar 2026
Views 33
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 3 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Perplexity 7 Amazonbot 6 Google 4 SEMrush 3 Unknown AI 2 Ahrefs 2
crawler 21 crawler_json 3
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Serve hashed filenames (app.abc123.js) with Cache-Control: immutable, max-age=31536000 — the hash changes when content changes, so the cache is always correct
📦 Applies To
any web
🔗 Prerequisites
🔍 Detection Hints
Static assets without content hash in filename; Cache-Control missing on CSS/JS; assets with very short max-age requiring frequent revalidation
Auto-detectable: ✓ Yes lighthouse curl webpack vite
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Low ✓ Auto-fixable Fix: Low Context: File

✓ schema.org compliant