Skip to content

Lazy Route Discovery manifest responses are cached indefinitely #13193

Closed as not planned
@jacobwgillespie

Description

@jacobwgillespie

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions