Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy Route Discovery manifest responses are cached indefinitely #13193

Open
jacobwgillespie opened this issue Mar 9, 2025 · 3 comments
Open
Labels

Comments

@jacobwgillespie
Copy link

I'm using React Router as a...

framework

Reproduction

By opening the network inspector and finding the request to /__manifest?..., you can note that the Cache-Control header is set to public, max-age=31536000, immutable - this is set in the server runtime here:

return Response.json(patches, {
headers: {
"Cache-Control": "public, max-age=31536000, immutable",
},
});

Image

System Info

System:
    OS: macOS 15.3.1
    CPU: (10) arm64 Apple M1 Max
    Memory: 3.90 GB / 32.00 GB
    Shell: 5.9 - /opt/homebrew/bin/zsh
  Binaries:
    Node: 22.12.0 - ~/.n/bin/node
    npm: 10.9.0 - ~/.n/bin/npm
    pnpm: 9.6.0 - ~/.n/bin/pnpm
    bun: 1.2.4 - /opt/homebrew/bin/bun
  Browsers:
    Chrome: 134.0.6998.44
    Edge: 134.0.3124.51
    Safari: 18.3
  npmPackages:
    @react-router/dev: ^7.0.0 => 7.3.0
    @react-router/express: ^7.0.0 => 7.3.0
    @react-router/fs-routes: ^7.0.0 => 7.3.0
    @react-router/node: ^7.0.0 => 7.3.0
    @react-router/serve: ^7.0.0 => 7.3.0
    react-router: ^7.0.0 => 7.3.0
    vite: ^6.2.1 => 6.2.1

Used Package Manager

pnpm

Expected Behavior

I'm not sure the original intention of Cache-Control header here, but doing so causes CDNs like CloudFront to cache the response which introduces two issues:

  1. After deploying a new application version, client-side navigation can break as stale versions of the manifest can be served. If the new/old pages have incompatible dependencies (e.g. upgrading React versions), this can break navigation in unexpected ways. This can also break the route discovery itself, leading to client-side navigations that return 404, but resolve on refresh.
  2. The recent PR Detect lazy route discovery manifest version mismatches and trigger reloads #13061 introduced detection for manifest version mismatches, however these mismatches are never detected with the indefinite cache header. Instead, the first request for that manifest/version is cached, and subsequent requests to that same path return the original cached response and are not able to reach the origin and evaluate the check for version skew.

Based on my understanding of the purpose of this manifest, I would expect it not to have a Cache-Control header at all, so that it can be properly evaluated when requested by the client.

Actual Behavior

The Cache-Control header is set to public, max-age=31536000, immutable and CDNs (like CloudFront) will indefinitely cache this response.

@jacobwgillespie jacobwgillespie changed the title Lazy Route Discovery manifests request are cached indefinitely Lazy Route Discovery manifest responses are cached indefinitely Mar 9, 2025
@jrestall
Copy link
Contributor

jrestall commented Mar 9, 2025

Shouldn't CloudFront cache based on all parameters in the query string and since the version parameter will change on each deployment, the cache will be invalidated?

The cache header should only be an issue if the CDN is configured to ignore the query string when caching requests.

@brophdawg11
Copy link
Contributor

@jrestall is correct - the caching is only there because the manifest version serves as the cachebuster for new deploys

@jacobwgillespie
Copy link
Author

What's the expected behavior with version mismatch detection? I'm thinking of the following case:

  1. Client code loads with version=1 and requests a manifest. This manifest is now cached in the CDN by key, including version=1.
  2. New version 2 is deployed on the backend
  3. Navigating around, the client requests the exact same manifest with the same version=1, which the CDN returns from its cache

I think what I'd want from that case is for request (3) to hit the origin so that the origin can inform the client that a new version exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants