Which project does this relate to?
Start
Describe the bug
When using TanStack Start with Nitro, requests to non-existent files under /assets/* are handled by the TanStack Start entry point (which may render the app shell, an error route, or any other response). Nitro then applies Cache-Control: public, max-age=31536000, immutable to that response because it matches the /assets/* path and returns a 200 status.
This is particularly dangerous in zero-downtime deployment scenarios where old and new server versions overlap briefly behind a load balancer:
- A browser fetches
index.html (never cached) and gets the new version, which references index-<newHash>.js.
- The CDN doesn't have
index-<newHash>.js yet, so it forwards the request to origin.
- The load balancer may route that request to the old server, which doesn't have that file.
- The old server handles the request through the TanStack Start entry point, returning a 200 and the shell. Nitro applies immutable cache headers to it.
- The CDN permanently caches the wrong response at that edge node. All users in that region get a broken app that can't self-heal.
This is not a theoretical problem — we've experienced it in production.
Current workaround
We created a catch-all TanStack Start route at /assets/$ that explicitly returns a 404:
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/assets/$")({
component: () => <div>Not found</div>,
server: {
handlers: {
ANY: () => {
return new Response("not found", { status: 404 });
},
},
},
});
This works because Nitro serves real static files before TanStack Start handlers run, and Nitro only applies immutable cache headers to 200 responses.
Your Example Website or App
--
Steps to Reproduce the Bug or Issue
--
Expected behavior
I would expect to either:
- Requests to non-existent assets under
/assets/* should return a 404, not be handled by the TanStack Start entry point. Nitro should not apply immutable cache headers to non-static responses.
OR
- Cache headers are only applied to assets built by vite. Non existent asset files can be routed to tanstack start, but they should not have the headers overwritten.
Screenshots or Videos
No response
Platform
Additional context
No response
Which project does this relate to?
Start
Describe the bug
When using TanStack Start with Nitro, requests to non-existent files under
/assets/*are handled by the TanStack Start entry point (which may render the app shell, an error route, or any other response). Nitro then appliesCache-Control: public, max-age=31536000, immutableto that response because it matches the/assets/*path and returns a 200 status.This is particularly dangerous in zero-downtime deployment scenarios where old and new server versions overlap briefly behind a load balancer:
index.html(never cached) and gets the new version, which referencesindex-<newHash>.js.index-<newHash>.jsyet, so it forwards the request to origin.This is not a theoretical problem — we've experienced it in production.
Current workaround
We created a catch-all TanStack Start route at
/assets/$that explicitly returns a 404:This works because Nitro serves real static files before TanStack Start handlers run, and Nitro only applies immutable cache headers to 200 responses.
Your Example Website or App
--
Steps to Reproduce the Bug or Issue
--
Expected behavior
I would expect to either:
/assets/*should return a 404, not be handled by the TanStack Start entry point. Nitro should not apply immutable cache headers to non-static responses.OR
Screenshots or Videos
No response
Platform
Additional context
No response