fix: preserve middleware Vary header in app pages#91039
fix: preserve middleware Vary header in app pages#91039Felipeness wants to merge 1 commit intovercel:canaryfrom
Conversation
The `handler` in `app-page.ts` used `res.setHeader('Vary', ...)` which
overwrote any Vary headers previously set by middleware or
`base-server.ts`'s `setVaryHeader()`. This caused CDN cache issues when
middleware added custom Vary values (e.g. `Vary: X-Foo`), as those
values were silently discarded.
The fix reads existing Vary values, merges them with the RSC headers
using case-insensitive deduplication, and sets the combined result.
The same pattern is applied to the edge runtime template
(`edge-ssr-app.ts`) which had an equivalent issue with `headers.set()`.
Closes vercel#85999
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
Alternative approaches consideredBefore settling on the read-merge-set approach, I considered two other alternatives: 1. Remove the
|
Summary
Fixes #85999
The
handlerfunction in the app page template (app-page.ts) usedres.setHeader('Vary', varyHeader)which overwrote any Vary headers previously set by middleware or bybase-server.ts'ssetVaryHeader(). This caused CDN cache issues because custom Vary values added by middleware (e.g.Vary: X-Foo) were silently discarded, leading to incorrect cache behavior.Root Cause
The request lifecycle for app pages is:
Vary: X-Foo) viaresponse.headers.append('vary', ...)base-server.ts:setVaryHeader()correctly usesres.appendHeader('vary', ...)to add RSC headers without overwritingapp-page.ts:handler()callsres.setHeader('Vary', varyHeader)which overwrites everything from steps 1 and 2The handler receives the raw
ServerResponse(notNodeNextResponse), sosetHeaderis a complete replacement.Fix
Instead of blindly overwriting, the fix reads the existing Vary header, merges it with the RSC values using case-insensitive deduplication (via
Set), and sets the combined result. This follows the same pattern used insend-response.tswhich already treatsvaryas a multi-value header, and the deduplication pattern frompatch-set-header.tsfor Set-Cookie.The same fix is applied to
edge-ssr-app.tswhich had an equivalent issue withheaders.set('Vary', varyHeader).Changes
packages/next/src/build/templates/app-page.ts: Replaceres.setHeader('Vary', varyHeader)with read-merge-set logic that preserves existing Vary valuespackages/next/src/build/templates/edge-ssr-app.ts: Add Vary-aware merging in the existing headers loop to avoid overwriting RSC Vary valuestest/e2e/vary-header/: Add app page test fixture and test case verifying middleware Vary headers are preserved in app pagesBefore (broken)
After (fixed)