Repository navigation
Revalidate cached responses with their ETag - #113
Merged
Merged
Conversation
When a cached response of a GET or HEAD request has an ETag, the requests that revalidate it, e.g. background revalidations, refetchOnFocus or requests with a cacheBuster, send the ETag in the If-None-Match header. If the server answers 304 Not Modified, the cached response is reused and cached again instead of being downloaded and parsed again, so it keeps its identity and e.g. useFetcher() doesn't rerender for it. It's enabled by default and can be disabled with etag: false. Requests with a custom fetcher only send ETags with etag: true, as fetchers may treat 304 responses as errors. Requests with an If-None-Match header of their own are sent as they are, and mutate() drops the ETag of the response it changes, as it no longer matches the data.
Core bundles 7.75 KB -> 8.75 KB, dist/react/index.mjs 10.75 KB -> 11.75 KB and dist/react/index.js 11.75 KB -> 12.75 KB. The limits fit this and the other PRs opened alongside it (abort signal fix, ETag revalidation, retry jitter and methods, onDownloadProgress, responseType, focusThrottleInterval, initialData and typed OpenAPI endpoints), which add about 1 KB together, so that they can be merged in any order.
size-limit report 📦
|
red-game-dev
approved these changes
Sep 27, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request adds ETag revalidation. When a cached response has an
ETag, the requests that revalidate it ask the server whether it changed, with anIf-None-Matchheader. If it didn't, the server answers304 Not Modifiedwithout a body, and the cached response is reused instead of being downloaded and parsed again, which saves bandwidth and spares rerenders.✨ Features & Improvements
If-None-Match, e.g. background revalidations of stale data,refetchOnFocus,refetchOnReconnect, polling or requests with acacheBuster. On304 Not Modified, the cached response is reused and cached again, so it's fresh for anotherstaleTime.selector passed toonResponseagain, so e.g.useFetcher()doesn't rerender after a background revalidation that is answered with304.cacheStore: a stale response restored after a page reload is revalidated with its ETag too.etagsetting, which istrueby default, so it can be disabled withetag: false.If-None-Matchheader of their own are sent as they are, and handle304responses themselves.fetcheronly send ETags withetag: true, as some fetchers, e.g. ones built on axios, treat304responses as errors.mutate()removes the ETag of the response it changes, as it no longer matches the data, so that a revalidation can't keep local changes that the server doesn't have.Revalidations of cached responses that have an ETag now send an
If-None-Matchheader. In browsers, the ETag of a cross-origin response can only be read when the server exposes it withAccess-Control-Expose-Headers, and such servers must also allow theIf-None-Matchheader withAccess-Control-Allow-Headers, otherwise the revalidation fails its CORS preflight. Settingetag: falserestores the previous behavior.🧪 Tests
304, background revalidation of stale data, revalidation while the cache is marked as fetching, no secondselectoronResponse, a new version of the response, and a response restored from a cache store.etag: false, with an ownIf-None-Matchheader, and with custom fetchers unless it's enabled.mutate()dropping the ETag.useFetcher()not rerendering after a focus revalidation answered with304, unlike after one answered with a response without ETag.📦 Bundle Size
The core bundles grow by about 0.2 KB (brotli). The size limits are raised to fit this and the other pull requests opened alongside it, so that they can be merged in any order.
Generated by Claude Code