diff --git a/packages/next/src/client/components/router-reducer/fetch-server-response.ts b/packages/next/src/client/components/router-reducer/fetch-server-response.ts index 2af83aa8a203c4..284a9874963307 100644 --- a/packages/next/src/client/components/router-reducer/fetch-server-response.ts +++ b/packages/next/src/client/components/router-reducer/fetch-server-response.ts @@ -27,7 +27,7 @@ import { } from '../app-router-headers' import { urlToUrlWithoutFlightMarker } from '../app-router' import { callServer } from '../../app-call-server' -import { PrefetchKind } from './router-reducer-types' +import { PrefetchKind, type PrefetchCacheEntry } from './router-reducer-types' import { hexHash } from '../../../shared/lib/hash' export type FetchServerResponseResult = [ diff --git a/packages/next/src/client/components/router-reducer/prefetch-cache-utils.ts b/packages/next/src/client/components/router-reducer/prefetch-cache-utils.ts index d91aaca4a9d7b2..a210ceb0e0acde 100644 --- a/packages/next/src/client/components/router-reducer/prefetch-cache-utils.ts +++ b/packages/next/src/client/components/router-reducer/prefetch-cache-utils.ts @@ -1,11 +1,12 @@ +import { createHrefFromUrl } from './create-href-from-url' +import { fetchServerResponse } from './fetch-server-response' import { PrefetchCacheEntryStatus, - type AppRouterState, type PrefetchCacheEntry, + PrefetchKind, + type ReadonlyReducerState, } from './router-reducer-types' -import { addPathPrefix } from '../../../shared/lib/router/utils/add-path-prefix' -import { pathHasPrefix } from '../../../shared/lib/router/utils/path-has-prefix' -import { createHrefFromUrl } from './create-href-from-url' +import { prefetchQueue } from './reducers/prefetch-reducer' /** * Creates a cache key for the router prefetch cache @@ -14,27 +15,177 @@ import { createHrefFromUrl } from './create-href-from-url' * @param nextUrl - an internal URL, primarily used for handling rewrites. Defaults to '/'. * @return The generated prefetch cache key. */ -export function createPrefetchCacheKey(url: URL, nextUrl: string | null) { +function createPrefetchCacheKey(url: URL, nextUrl?: string | null) { const pathnameFromUrl = createHrefFromUrl( url, // Ensures the hash is not part of the cache key as it does not impact the server fetch false ) - // delimit the prefix so we don't conflict with other pages - const nextUrlPrefix = `${nextUrl}%` - - // Route interception depends on `nextUrl` values which aren't a 1:1 mapping to a URL - // The cache key that we store needs to use `nextUrl` to properly distinguish cache entries - if (nextUrl && !pathHasPrefix(pathnameFromUrl, nextUrl)) { - return addPathPrefix(pathnameFromUrl, nextUrlPrefix) + // nextUrl is used as a cache key delimiter since entries can vary based on the Next-URL header + if (nextUrl) { + return `${nextUrl}%${pathnameFromUrl}` } return pathnameFromUrl } +/** + * Returns a prefetch cache entry if one exists. Otherwise creates a new one and enqueues a fetch request + * to retrieve the prefetch data from the server. + */ +export function getOrCreatePrefetchCacheEntry({ + url, + nextUrl, + tree, + buildId, + prefetchCache, + kind, +}: Pick< + ReadonlyReducerState, + 'nextUrl' | 'prefetchCache' | 'tree' | 'buildId' +> & { + url: URL + kind?: PrefetchKind +}): PrefetchCacheEntry { + let existingCacheEntry: PrefetchCacheEntry | undefined = undefined + // We first check if there's a more specific interception route prefetch entry + // This is because when we detect a prefetch that corresponds with an interception route, we prefix it with nextUrl (see `createPrefetchCacheKey`) + // to avoid conflicts with other pages that may have the same URL but render different things depending on the `Next-URL` header. + const interceptionCacheKey = createPrefetchCacheKey(url, nextUrl) + const interceptionData = prefetchCache.get(interceptionCacheKey) + + if (interceptionData) { + existingCacheEntry = interceptionData + } else { + // If we dont find a more specific interception route prefetch entry, we check for a regular prefetch entry + const prefetchCacheKey = createPrefetchCacheKey(url) + const prefetchData = prefetchCache.get(prefetchCacheKey) + if (prefetchData) { + existingCacheEntry = prefetchData + } + } + + if (existingCacheEntry) { + // when `kind` is provided, an explicit prefetch was requested. + // if the requested prefetch is "full" and the current cache entry wasn't, we want to re-prefetch with the new intent + if ( + kind && + existingCacheEntry.kind !== PrefetchKind.FULL && + kind === PrefetchKind.FULL + ) { + return createLazyPrefetchEntry({ + tree, + url, + buildId, + nextUrl, + prefetchCache, + kind, + }) + } + + // Grab the latest status of the cache entry and update it + existingCacheEntry.status = getPrefetchEntryCacheStatus(existingCacheEntry) + + // If the existing cache entry was marked as temporary, it means it was lazily created when attempting to get an entry, + // where we didn't have the prefetch intent. Now that we have the intent (in `kind`), we want to update the entry to the more accurate kind. + if (kind && existingCacheEntry.kind === PrefetchKind.TEMPORARY) { + existingCacheEntry.kind = kind + } + + // We've determined that the existing entry we found is still valid, so we return it. + return existingCacheEntry + } + + // If we didn't return an entry, create a new one. + return createLazyPrefetchEntry({ + tree, + url, + buildId, + nextUrl, + prefetchCache, + kind: + kind || + // in dev, there's never gonna be a prefetch entry so we want to prefetch here + (process.env.NODE_ENV === 'development' + ? PrefetchKind.AUTO + : PrefetchKind.TEMPORARY), + }) +} + +function prefixExistingPrefetchCacheEntry({ + url, + nextUrl, + prefetchCache, +}: Pick & { + url: URL +}) { + const existingCacheKey = createPrefetchCacheKey(url) + const existingCacheEntry = prefetchCache.get(existingCacheKey) + if (!existingCacheEntry) { + // no-op -- there wasn't an entry to move + return + } + + const newCacheKey = createPrefetchCacheKey(url, nextUrl) + prefetchCache.set(newCacheKey, existingCacheEntry) + prefetchCache.delete(existingCacheKey) +} + +/** + * Creates a prefetch entry for data that has not been resolved. This will add the prefetch request to a promise queue. + */ +function createLazyPrefetchEntry({ + url, + kind, + tree, + nextUrl, + buildId, + prefetchCache, +}: Pick< + ReadonlyReducerState, + 'nextUrl' | 'tree' | 'buildId' | 'prefetchCache' +> & { + url: URL + kind: PrefetchKind +}): PrefetchCacheEntry { + const prefetchCacheKey = createPrefetchCacheKey(url) + + // initiates the fetch request for the prefetch and attaches a listener + // to the promise to update the prefetch cache entry when the promise resolves (if necessary) + const data = prefetchQueue.enqueue(() => + fetchServerResponse(url, tree, nextUrl, buildId, kind).then( + (prefetchResponse) => { + // TODO: `fetchServerResponse` should be more tighly coupled to these prefetch cache operations + // to avoid drift between this cache key prefixing logic + // (which is currently directly influenced by the server response) + const [, , , intercepted] = prefetchResponse + if (intercepted) { + prefixExistingPrefetchCacheEntry({ url, nextUrl, prefetchCache }) + } + + return prefetchResponse + } + ) + ) + + const prefetchEntry = { + treeAtTimeOfPrefetch: tree, + data, + kind, + prefetchTime: Date.now(), + lastUsedTime: null, + key: prefetchCacheKey, + status: PrefetchCacheEntryStatus.fresh, + } + + prefetchCache.set(prefetchCacheKey, prefetchEntry) + + return prefetchEntry +} + export function prunePrefetchCache( - prefetchCache: AppRouterState['prefetchCache'] + prefetchCache: ReadonlyReducerState['prefetchCache'] ) { for (const [href, prefetchCacheEntry] of prefetchCache) { if ( @@ -49,7 +200,7 @@ export function prunePrefetchCache( const FIVE_MINUTES = 5 * 60 * 1000 const THIRTY_SECONDS = 30 * 1000 -export function getPrefetchEntryCacheStatus({ +function getPrefetchEntryCacheStatus({ kind, prefetchTime, lastUsedTime, diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx index b7cf15ce625f4c..2f145df1a8c58a 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx @@ -260,6 +260,7 @@ describe('navigateReducer', () => { "kind": "temporary", "lastUsedTime": 1690329600000, "prefetchTime": 1690329600000, + "status": "fresh", "treeAtTimeOfPrefetch": [ "", { @@ -455,6 +456,7 @@ describe('navigateReducer', () => { "kind": "temporary", "lastUsedTime": 1690329600000, "prefetchTime": 1690329600000, + "status": "fresh", "treeAtTimeOfPrefetch": [ "", { @@ -893,6 +895,7 @@ describe('navigateReducer', () => { "kind": "temporary", "lastUsedTime": 1690329600000, "prefetchTime": 1690329600000, + "status": "fresh", "treeAtTimeOfPrefetch": [ "", { @@ -1120,6 +1123,7 @@ describe('navigateReducer', () => { "kind": "auto", "lastUsedTime": 1690329600000, "prefetchTime": 1690329600000, + "status": "fresh", "treeAtTimeOfPrefetch": [ "", { @@ -1375,6 +1379,7 @@ describe('navigateReducer', () => { "kind": "temporary", "lastUsedTime": 1690329600000, "prefetchTime": 1690329600000, + "status": "fresh", "treeAtTimeOfPrefetch": [ "", { @@ -1719,6 +1724,7 @@ describe('navigateReducer', () => { "kind": "temporary", "lastUsedTime": 1690329600000, "prefetchTime": 1690329600000, + "status": "fresh", "treeAtTimeOfPrefetch": [ "", { diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts index 001fa3aad2ece7..d99d420ddf3c50 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts @@ -17,7 +17,7 @@ import type { ReadonlyReducerState, ReducerState, } from '../router-reducer-types' -import { PrefetchKind, PrefetchCacheEntryStatus } from '../router-reducer-types' +import { PrefetchCacheEntryStatus } from '../router-reducer-types' import { handleMutable } from '../handle-mutable' import { applyFlightData } from '../apply-flight-data' import { prefetchQueue } from './prefetch-reducer' @@ -28,12 +28,9 @@ import { updateCacheNodeOnNavigation, } from '../ppr-navigations' import { - createPrefetchCacheKey, - getPrefetchCacheEntry, - getPrefetchEntryCacheStatus, + getOrCreatePrefetchCacheEntry, prunePrefetchCache, - createPrefetchCacheEntry, -} from './prefetch-cache-utils' +} from '../prefetch-cache-utils' export function handleExternalUrl( state: ReadonlyReducerState, @@ -128,27 +125,19 @@ function navigateReducer_noPPR( return handleExternalUrl(state, mutable, url.toString(), pendingPush) } - let prefetchValues = getPrefetchCacheEntry(url, state) - // If we don't have a prefetch value, we need to create one - if (!prefetchValues) { - const cacheKey = createPrefetchCacheKey(url) - prefetchValues = createPrefetchCacheEntry({ - state, - url, - // in dev, there's never gonna be a prefetch entry so we want to prefetch here - kind: - process.env.NODE_ENV === 'development' - ? PrefetchKind.AUTO - : PrefetchKind.TEMPORARY, - prefetchCacheKey: cacheKey, - }) - - state.prefetchCache.set(cacheKey, prefetchValues) - } - - const prefetchEntryCacheStatus = getPrefetchEntryCacheStatus(prefetchValues) + const prefetchValues = getOrCreatePrefetchCacheEntry({ + url, + nextUrl: state.nextUrl, + tree: state.tree, + buildId: state.buildId, + prefetchCache: state.prefetchCache, + }) + const { + treeAtTimeOfPrefetch, + data, + status: prefetchEntryCacheStatus, + } = prefetchValues - const { treeAtTimeOfPrefetch, data } = prefetchValues prefetchQueue.bump(data) return data.then( @@ -305,27 +294,19 @@ function navigateReducer_PPR( return handleExternalUrl(state, mutable, url.toString(), pendingPush) } - let prefetchValues = getPrefetchCacheEntry(url, state) - // If we don't have a prefetch value, we need to create one - if (!prefetchValues) { - const cacheKey = createPrefetchCacheKey(url) - prefetchValues = createPrefetchCacheEntry({ - state, - url, - // in dev, there's never gonna be a prefetch entry so we want to prefetch here - kind: - process.env.NODE_ENV === 'development' - ? PrefetchKind.AUTO - : PrefetchKind.TEMPORARY, - prefetchCacheKey: cacheKey, - }) - - state.prefetchCache.set(cacheKey, prefetchValues) - } - - const prefetchEntryCacheStatus = getPrefetchEntryCacheStatus(prefetchValues) + const prefetchValues = getOrCreatePrefetchCacheEntry({ + url, + nextUrl: state.nextUrl, + tree: state.tree, + buildId: state.buildId, + prefetchCache: state.prefetchCache, + }) + const { + treeAtTimeOfPrefetch, + data, + status: prefetchEntryCacheStatus, + } = prefetchValues - const { treeAtTimeOfPrefetch, data } = prefetchValues prefetchQueue.bump(data) return data.then( diff --git a/packages/next/src/client/components/router-reducer/reducers/prefetch-cache-utils.ts b/packages/next/src/client/components/router-reducer/reducers/prefetch-cache-utils.ts deleted file mode 100644 index e96432f4a53423..00000000000000 --- a/packages/next/src/client/components/router-reducer/reducers/prefetch-cache-utils.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { createHrefFromUrl } from '../create-href-from-url' -import { fetchServerResponse } from '../fetch-server-response' -import type { - PrefetchCacheEntry, - PrefetchKind, - ReadonlyReducerState, -} from '../router-reducer-types' -import { prefetchQueue } from './prefetch-reducer' - -/** - * Creates a cache key for the router prefetch cache - * - * @param url - The URL being navigated to - * @param nextUrl - an internal URL, primarily used for handling rewrites. Defaults to '/'. - * @return The generated prefetch cache key. - */ -export function createPrefetchCacheKey(url: URL, nextUrl?: string | null) { - const pathnameFromUrl = createHrefFromUrl( - url, - // Ensures the hash is not part of the cache key as it does not impact the server fetch - false - ) - - // delimit the prefix so we don't conflict with other pages - - // Route interception depends on `nextUrl` values which aren't a 1:1 mapping to a URL - // The cache key that we store needs to use `nextUrl` to properly distinguish cache entries - if (nextUrl) { - return `${nextUrl}%${pathnameFromUrl}` - } - - return pathnameFromUrl -} - -export function getPrefetchCacheEntry( - url: URL, - state: ReadonlyReducerState -): PrefetchCacheEntry | undefined { - // We first check if there's a more specific interception route prefetch entry - // This is because when we detect a prefetch that corresponds with an interception route, we prefix it with nextUrl (see `createPrefetchCacheKey`) - // to avoid conflicts with other pages that may have the same URL but render different things depending on the `Next-URL` header. - const interceptionCacheKey = createPrefetchCacheKey(url, state.nextUrl) - const interceptionData = state.prefetchCache.get(interceptionCacheKey) - - if (interceptionData) { - return interceptionData - } - - // If we dont find a more specific interception route prefetch entry, we check for a regular prefetch entry - const prefetchCacheKey = createPrefetchCacheKey(url) - return state.prefetchCache.get(prefetchCacheKey) -} - -export function createPrefetchCacheEntry({ - state, - url, - kind, - prefetchCacheKey, -}: { - state: ReadonlyReducerState - url: URL - kind: PrefetchKind - prefetchCacheKey: string -}): PrefetchCacheEntry { - // initiates the fetch request for the prefetch and attaches a listener - // to the promise to update the prefetch cache entry when the promise resolves (if necessary) - const getPrefetchData = () => - fetchServerResponse( - url, - state.tree, - state.nextUrl, - state.buildId, - kind - ).then((prefetchResponse) => { - /* [flightData, canonicalUrlOverride, postpone, intercept] */ - const [, , , intercept] = prefetchResponse - const existingPrefetchEntry = state.prefetchCache.get(prefetchCacheKey) - // If we discover that the prefetch corresponds with an interception route, we want to move it to - // a prefixed cache key to avoid clobbering an existing entry. - if (intercept && existingPrefetchEntry) { - const prefixedCacheKey = createPrefetchCacheKey(url, state.nextUrl) - state.prefetchCache.set(prefixedCacheKey, existingPrefetchEntry) - state.prefetchCache.delete(prefetchCacheKey) - } - - return prefetchResponse - }) - - const data = prefetchQueue.enqueue(getPrefetchData) - - return { - treeAtTimeOfPrefetch: state.tree, - data, - kind, - prefetchTime: Date.now(), - lastUsedTime: null, - key: prefetchCacheKey, - } -} - -export function prunePrefetchCache( - prefetchCache: ReadonlyReducerState['prefetchCache'] -) { - for (const [href, prefetchCacheEntry] of prefetchCache) { - if ( - getPrefetchEntryCacheStatus(prefetchCacheEntry) === - PrefetchCacheEntryStatus.expired - ) { - prefetchCache.delete(href) - } - } -} - -const FIVE_MINUTES = 5 * 60 * 1000 -const THIRTY_SECONDS = 30 * 1000 - -export enum PrefetchCacheEntryStatus { - fresh = 'fresh', - reusable = 'reusable', - expired = 'expired', - stale = 'stale', -} - -export function getPrefetchEntryCacheStatus({ - kind, - prefetchTime, - lastUsedTime, -}: PrefetchCacheEntry): PrefetchCacheEntryStatus { - // if the cache entry was prefetched or read less than 30s ago, then we want to re-use it - if (Date.now() < (lastUsedTime ?? prefetchTime) + THIRTY_SECONDS) { - return lastUsedTime - ? PrefetchCacheEntryStatus.reusable - : PrefetchCacheEntryStatus.fresh - } - - // if the cache entry was prefetched less than 5 mins ago, then we want to re-use only the loading state - if (kind === 'auto') { - if (Date.now() < prefetchTime + FIVE_MINUTES) { - return PrefetchCacheEntryStatus.stale - } - } - - // if the cache entry was prefetched less than 5 mins ago and was a "full" prefetch, then we want to re-use it "full - if (kind === 'full') { - if (Date.now() < prefetchTime + FIVE_MINUTES) { - return PrefetchCacheEntryStatus.reusable - } - } - - return PrefetchCacheEntryStatus.expired -} diff --git a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx index 88efb764f6b3ad..e7071f47451644 100644 --- a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx @@ -4,7 +4,11 @@ import type { FlightData } from '../../../../server/app-render/types' import type { FlightRouterState } from '../../../../server/app-render/types' import type { CacheNode } from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' -import { ACTION_PREFETCH, PrefetchKind } from '../router-reducer-types' +import { + ACTION_PREFETCH, + PrefetchCacheEntryStatus, + PrefetchKind, +} from '../router-reducer-types' import type { PrefetchAction } from '../router-reducer-types' import { prefetchReducer } from './prefetch-reducer' import { fetchServerResponse } from '../fetch-server-response' @@ -127,6 +131,7 @@ describe('prefetchReducer', () => { url, initialTree, null, + state.buildId, PrefetchKind.AUTO ) const action: PrefetchAction = { @@ -153,6 +158,7 @@ describe('prefetchReducer', () => { kind: PrefetchKind.AUTO, lastUsedTime: null, prefetchTime: expect.any(Number), + status: PrefetchCacheEntryStatus.fresh, treeAtTimeOfPrefetch: [ '', { @@ -310,6 +316,7 @@ describe('prefetchReducer', () => { prefetchTime: expect.any(Number), kind: PrefetchKind.AUTO, lastUsedTime: null, + status: PrefetchCacheEntryStatus.fresh, treeAtTimeOfPrefetch: [ '', { diff --git a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.ts index 851a648538303a..813de317aa5930 100644 --- a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.ts @@ -3,15 +3,12 @@ import type { ReducerState, ReadonlyReducerState, } from '../router-reducer-types' -import { PrefetchKind } from '../router-reducer-types' import { NEXT_RSC_UNION_QUERY } from '../../app-router-headers' import { PromiseQueue } from '../../promise-queue' import { - createPrefetchCacheKey, - createPrefetchCacheEntry, - getPrefetchCacheEntry, + getOrCreatePrefetchCacheEntry, prunePrefetchCache, -} from './prefetch-cache-utils' +} from '../prefetch-cache-utils' export const prefetchQueue = new PromiseQueue(5) @@ -25,43 +22,14 @@ export function prefetchReducer( const { url } = action url.searchParams.delete(NEXT_RSC_UNION_QUERY) - const cacheEntry = getPrefetchCacheEntry(url, state) - - if (cacheEntry) { - /** - * If the cache entry present was marked as temporary, it means that we prefetched it from the navigate reducer, - * where we didn't have the prefetch intent. We want to update it to the new, more accurate, kind here. - */ - if (cacheEntry.kind === PrefetchKind.TEMPORARY) { - state.prefetchCache.set(cacheEntry.key, { - ...cacheEntry, - kind: action.kind, - }) - } - - /** - * if the prefetch action was a full prefetch and that the current cache entry wasn't one, we want to re-prefetch, - * otherwise we can re-use the current cache entry - **/ - if ( - !( - cacheEntry.kind === PrefetchKind.AUTO && - action.kind === PrefetchKind.FULL - ) - ) { - return state - } - } - - const prefetchCacheKey = createPrefetchCacheKey(url) - const newEntry = createPrefetchCacheEntry({ - state, + getOrCreatePrefetchCacheEntry({ url, + nextUrl: state.nextUrl, + prefetchCache: state.prefetchCache, kind: action.kind, - prefetchCacheKey, + tree: state.tree, + buildId: state.buildId, }) - state.prefetchCache.set(prefetchCacheKey, newEntry) - return state } diff --git a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx index 3ad5b14e2b146a..76a6a8708fe674 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx @@ -448,6 +448,7 @@ describe('serverPatchReducer', () => { "kind": "temporary", "lastUsedTime": 1690329600000, "prefetchTime": 1690329600000, + "status": "fresh", "treeAtTimeOfPrefetch": [ "", { diff --git a/packages/next/src/client/components/router-reducer/router-reducer-types.ts b/packages/next/src/client/components/router-reducer/router-reducer-types.ts index bd6c90f11f6d27..9268aca1fa0f60 100644 --- a/packages/next/src/client/components/router-reducer/router-reducer-types.ts +++ b/packages/next/src/client/components/router-reducer/router-reducer-types.ts @@ -203,6 +203,7 @@ export type PrefetchCacheEntry = { prefetchTime: number lastUsedTime: number | null key: string + status: PrefetchCacheEntryStatus } export enum PrefetchCacheEntryStatus { diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index 94a5356a6959ce..a3d173669ce582 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -2035,26 +2035,6 @@ export default abstract class Server { } if (isAppPath) { - res.setHeader('vary', RSC_VARY_HEADER) - - const couldBeIntercepted = this.interceptionRouteRewrites?.some( - (rewrite) => { - return new RegExp(rewrite.regex).test(resolvedUrlPathname) - } - ) - - // Interception route responses can vary based on the `Next-URL` header as they're rewritten to different components. - // This means that multiple route interception responses can resolve to the same URL. We use the Vary header to signal this - // behavior to the client so that it can properly cache the response. - // If the request that we're handling is one that could have a different response based on the `Next-URL` header, or if - // we're handling an interception route, then we include `Next-URL` in the Vary header. - if ( - couldBeIntercepted || - isInterceptionRouteAppPath(resolvedUrlPathname) - ) { - res.setHeader('vary', `${RSC_VARY_HEADER}, ${NEXT_URL}`) - } - if (!this.renderOpts.dev && !isPreviewMode && isSSG && isRSCRequest) { // If this is an RSC request but we aren't in minimal mode, then we mark // that this is a data request so that we can generate the flight data