Skip to content

Fix prefetched data being downloaded twice - #261

Merged
brookslybrand merged 3 commits into
mainfrom
fix-prefetch-vary
Oct 8, 2026
Merged

brookslybrand merged 3 commits into
mainfrom
fix-prefetch-vary

Conversation

@brookslybrand

@brookslybrand brookslybrand commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Hovering a menu link prefetches the next page's data, but clicking it downloads the data again. This happens on production today, not just in the RSC PRs.

The doc route sets Vary: Accept so the same URL can serve HTML or Markdown (#248). Route headers also apply to .data responses, so those get Vary: Accept too. The prefetch is a <link rel="prefetch">, which Chrome sends with Accept: text/html,…, while the navigation is a fetch() with Accept: */*. Because of Vary: Accept, the browser won't reuse the prefetched response for a request with a different Accept header, so it downloads the data again.

Data URLs (.data, and .rsc once #259 lands) are never content-negotiated, so the server now ignores Accept on GET/HEAD requests to them and drops Accept from Vary on the response. It's a fetch-router middleware in server.ts, not React Router middleware, because React Router strips the extension before its middleware and headers run, so they can't tell a data request from a document request. The app request is now built from context.headers, so header changes made by middleware reach React Router.

Removing Accept from the request matters, not only the Vary change. Right now curl -H "Accept: text/markdown" https://reactrouter.com/start/framework/routing.data returns Markdown. With Vary: Accept gone, Fly's Varnish could cache that Markdown under the .data URL and serve it to everyone's navigations.

Tested on a production build:

Request Before After
/start/framework/routing.data Vary: Cookie, Accept Vary: Cookie
.data with Accept: text/markdown Markdown Data
/start/framework/routing Vary: Cookie, Accept Unchanged
/start/framework/routing with Accept: text/markdown Markdown Unchanged
Prefetch, then fetch() of the same .data URL 2 server hits 1 server hit (fetch served from cache)

@brookslybrand
brookslybrand merged commit d1a7f4b into main Oct 8, 2026
1 check passed
@brookslybrand
brookslybrand deleted the fix-prefetch-vary branch October 8, 2026 17:00
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.

1 participant