Skip to content

fix: add cache-bust query param to Vite asset URLs for immutable caching#3761

Merged
bartlomieju merged 5 commits intomainfrom
fix/vite-asset-cache-headers
Apr 9, 2026
Merged

fix: add cache-bust query param to Vite asset URLs for immutable caching#3761
bartlomieju merged 5 commits intomainfrom
fix/vite-asset-cache-headers

Conversation

@bartlomieju
Copy link
Copy Markdown
Member

@bartlomieju bartlomieju commented Apr 9, 2026

Summary

  • Vite-built assets (JS chunks, CSS) were served with Cache-Control: no-cache, no-store because the static files middleware only recognized builder-path assets (under /_fresh/js/{BUILD_ID}/) as immutable
  • Adds an immutable flag to FileSnapshot, StaticFile, and PendingStaticFile interfaces
  • The Vite plugin marks manifest chunks and CSS as immutable: true (these have content-hashed filenames), while public directory files remain non-immutable
  • The static files middleware checks file.immutable alongside the existing path and query param checks

Closes #3282

Test plan

  • All 10 static files unit tests pass
  • All 31 Vite build integration tests pass (including new cache headers test)
  • All 22 builder tests pass
  • New test: vite build - asset cache headers on CSS and JS — fetches CSS and JS asset URLs from a page with islands and verifies they return Cache-Control: public, max-age=31536000, immutable

🤖 Generated with Claude Code

Vite-built assets (JS chunks, CSS) were served with no-cache headers
because the static files middleware only recognized builder-path assets
(under /_fresh/js/{BUILD_ID}/) as immutable.

Add an `immutable` flag to the file snapshot interface. The Vite plugin
marks manifest chunks and CSS as immutable (content-hashed filenames),
while public directory files remain non-immutable. The middleware checks
this flag alongside the existing path and query param checks.

Closes #3282

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bartlomieju bartlomieju force-pushed the fix/vite-asset-cache-headers branch from 055be3c to f80b116 Compare April 9, 2026 10:31
bartlomieju and others added 4 commits April 9, 2026 13:02
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The preact_hooks.ts changes from the initial URL-based approach were
accidentally left in the commit. These added ?__frsh_c query params
to JS module imports which broke builder-path tests. The immutable
flag approach doesn't need any changes to import URLs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The registerStaticFile function in server_entry.ts was dropping the
hash and immutable fields when adding files to the snapshot's
staticFiles Map. On Windows, this caused server-entry assets to
overwrite snapshot entries, losing the immutable flag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On Windows, path.relative returns backslashes but the registeredPaths
Set uses forward slashes from Vite's manifest. This caused every file
to be re-registered without the immutable flag, overwriting the
original entries from the manifest scan.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bartlomieju
Copy link
Copy Markdown
Member Author

Reviewed the diff — this looks good to merge:

  • immutable flag is opt-in on the file interfaces, no breaking changes
  • Only content-hashed Vite assets get marked, public dir files correctly excluded
  • Static files middleware check is additive alongside existing BUILD_ID logic
  • Bonus: hash passthrough fix in registerStaticFile and Windows path separator fix in server_snapshot.ts
  • Good test — extracts real CSS/JS asset URLs from rendered HTML and verifies Cache-Control: public, max-age=31536000, immutable
  • All CI green including Windows

@bartlomieju bartlomieju merged commit 14f8955 into main Apr 9, 2026
9 checks passed
@bartlomieju bartlomieju deleted the fix/vite-asset-cache-headers branch April 9, 2026 14:46
bartlomieju added a commit that referenced this pull request Apr 27, 2026
- Bump `@fresh/core` to `2.3.1`
- Bump `@fresh/init` to `2.3.1`
- Bump `@fresh/update` to `2.3.1`
- Bump `@fresh/plugin-vite` from `1.0.8` to `1.1.0`
- Bump `@fresh/plugin-tailwind-v3` from `1.0.1` to `1.1.0`

The plugin packages were not published during the 2.3.0 release despite
having significant changes merged. Notable fixes in `@fresh/plugin-vite`
include immutable caching for Vite-built assets (#3761), CSS modules in
layouts (#3764), and multiple `staticDir` support (#3759).

Also updates `FRESH_VITE_PLUGIN` version constant in `@fresh/init` so
new projects scaffold with the correct plugin version.

Closes #3784
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.

vite: asset cache headers not working

1 participant