Skip to content

Cache-bust app CSS/JS so CF's 4h browser cache can't serve stale code#47

Merged
falkoro merged 1 commit into
masterfrom
feat/cache-bust-assets
Jun 5, 2026
Merged

Cache-bust app CSS/JS so CF's 4h browser cache can't serve stale code#47
falkoro merged 1 commit into
masterfrom
feat/cache-bust-assets

Conversation

@falkoro
Copy link
Copy Markdown
Owner

@falkoro falkoro commented Jun 5, 2026

The bug (why "I deployed but still see the old UI")

  • Origin sends Cache-Control: no-cache on /assets/* (meant to be always-fresh for hot-reload).
  • Cloudflare overrides it to max-age=14400 (4h) and caches the css/js at the edge → browsers hold the old app.css/render.js for up to 4 hours.
  • The HTML page is no-store (always fresh) — confirmed via curl.

Fix

Append a content-version query to the app's own css/js URLs: app.css?v=<newest mtime>. Since the HTML is always fresh, a reload now points at an asset URL the browser has never cached → guaranteed fresh css/js. Version = newest mtime among the app's own files, so it auto-bumps on every deploy and every hot-reloaded CSS edit.

  • Vendored xterm.* left cacheable (they never change).
  • The asset route already strips the query (serves by filename), so no route change.

Verified live

  • HTML serves /assets/app.css?v=1780690125; the versioned URL returns 200 with the new CSS.
  • Public page via CF carries the versioned URL; HTML is no-store.

🤖 Generated with Claude Code

… code

The origin sends Cache-Control: no-cache on /assets/*, but Cloudflare
overrides it to max-age=14400 and caches the css/js — so browsers held
old code for up to 4h ("I deployed but still see the old UI"). The HTML
is no-store (always fresh), so append a content-version query
(?v=<newest mtime>) to the app's own css/js URLs: a fresh page now
always points at an asset URL the browser hasn't cached. Vendored
xterm.* are left cacheable. The asset route already ignores the query
(serves by filename), so no route change needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 5, 2026 20:41
@falkoro falkoro merged commit 5f8cde2 into master Jun 5, 2026
@falkoro falkoro deleted the feat/cache-bust-assets branch June 5, 2026 20:41
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds cache-busting for dashboard-served CSS/JS by appending a version query param derived from asset mtimes, preventing stale CDN/browser caches after deploys.

Changes:

  • Compute an asset version string from the newest mtime among a fixed list of “versioned” assets.
  • Post-process the generated dashboard HTML to append ?v=... to matching /assets/... URLs.
  • Refactor dashboard() to build HTML, then apply cache-busting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/pages.rs
Comment on lines +35 to +46
fn asset_ver(config: &Config) -> String {
let dir = config.root_dir.join("public");
let mut newest: u64 = 0;
for name in VERSIONED_ASSETS {
if let Ok(t) = std::fs::metadata(dir.join(name)).and_then(|m| m.modified()) {
if let Ok(d) = t.duration_since(std::time::UNIX_EPOCH) {
newest = newest.max(d.as_secs());
}
}
}
newest.to_string()
}
Comment thread src/pages.rs
Comment on lines +19 to +20
let v = asset_ver(config);
let html = format!(
Comment thread src/pages.rs
Comment on lines +48 to 54
fn bust_assets(html: &str, v: &str) -> String {
let mut out = html.to_string();
for name in VERSIONED_ASSETS {
out = out.replace(&format!("/assets/{name}\""), &format!("/assets/{name}?v={v}\""));
}
out
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants