[PR #488] fix: replay render-time response headers for cached App Router responses #610

Open
opened 2026-05-06 13:09:03 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/488
Author: @JaredStowell
Created: 3/12/2026
Status: 🔄 Open

Base: mainHead: jstowell/render-response-headers-cache-parity


📝 Commits (10+)

  • 86aedca Add tests for render headers
  • 7eff581 Review CI failures and plan fixes
  • 18f9483 Review and fix CI failures
  • d33fdb2 Fix ISR generateRscEntry tests
  • a2cddae Fix cached header replay parity
  • 36f6ce4 Fix cached header replay duplicates
  • 17c5808 Fix cached header replay
  • 03ac94d Fix render response header parity regressions
  • 09b70a5 Restore app router parity after rebase
  • ca4e4c6 Fix remaining CI failures

📊 Changes

41 files changed (+2976 additions, -825 deletions)

View changed files

📝 benchmarks/vinext-rolldown/tsconfig.json (+2 -1)
📝 benchmarks/vinext/tsconfig.json (+2 -1)
📝 packages/vinext/src/entries/app-rsc-entry.ts (+203 -103)
📝 packages/vinext/src/index.ts (+6 -0)
📝 packages/vinext/src/server/app-page-cache.ts (+101 -13)
📝 packages/vinext/src/server/app-page-render.ts (+32 -3)
📝 packages/vinext/src/server/app-page-response.ts (+97 -29)
📝 packages/vinext/src/server/app-page-stream.ts (+0 -1)
📝 packages/vinext/src/server/app-route-handler-execution.ts (+24 -12)
📝 packages/vinext/src/server/app-route-handler-response.ts (+112 -25)
📝 packages/vinext/src/server/isr-cache.ts (+2 -1)
📝 packages/vinext/src/shims/headers.ts (+196 -19)
📝 packages/vinext/src/shims/unified-request-context.ts (+15 -7)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+1179 -569)
📝 tests/app-page-boundary-render.test.ts (+2 -2)
📝 tests/app-page-cache.test.ts (+68 -2)
📝 tests/app-page-render.test.ts (+7 -0)
📝 tests/app-page-response.test.ts (+20 -4)
📝 tests/app-page-stream.test.ts (+2 -2)
📝 tests/app-route-handler-execution.test.ts (+24 -25)

...and 21 more files

📄 Description

Bring cached App Router response-header behavior closer to Next.js by preserving and replaying render-time response headers losslessly.

This fixes parity for headers that are mutated during render rather than after the final Response is created, including:

  • Vary
  • Set-Cookie
  • repeated auth challenge headers like WWW-Authenticate / Proxy-Authenticate
  • custom response headers written through the shims
  • headers emitted during async/suspended render work

What changed

  • added a render-response-header collector to the request-scoped headers ALS
  • changed the collector to use a multivalue-safe internal representation instead of native Headers, so repeated non-cookie headers are not flattened before caching
  • persisted cached App Router render headers in a lossless serialized form, preserving repeated values for any header key
  • replaced cached replay/header application with an explicit merge helper
  • applied deterministic merge rules across cached render headers, framework headers, and middleware headers:
    • set-cookie: append and preserve order
    • vary: append/merge, never overwrite
    • www-authenticate / proxy-authenticate: append repeated values
    • all other headers: cached render headers are fallback-only; framework and middleware can override
  • replayed cached render headers on cached HIT and STALE responses
  • applied the same merge path to direct RSC MISS responses
  • kept special-response branches aligned so redirects and HTTP fallback responses preserve render-time headers and middleware response headers

Tests

Added and updated coverage for:

  • HTML MISS / HIT / STALE / post-regeneration HIT
  • direct RSC-first MISS / HIT
  • late async render-time header mutation
  • repeated render-time Vary replay across cached responses
  • repeated WWW-Authenticate replay/merge behavior
  • multi-value Set-Cookie replay
  • middleware override, Vary, and cookie merge behavior on cached responses
  • redirects thrown during metadata resolution
  • layout-level notFound() special-response handling
  • public cookies() and draftMode() serialization into render response headers

Validation

Ran:

  • pnpm test tests/shims.test.ts tests/isr-cache.test.ts tests/app-router.test.ts tests/entry-templates.test.ts -u
  • pnpm run fmt
  • pnpm run typecheck

Notes

This does not special-case cookies. The cached artifact is a general render-time response-header payload with lossless multi-value handling for repeated headers.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/488 **Author:** [@JaredStowell](https://github.com/JaredStowell) **Created:** 3/12/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `jstowell/render-response-headers-cache-parity` --- ### 📝 Commits (10+) - [`86aedca`](https://github.com/cloudflare/vinext/commit/86aedca02d571406b900d7cd1572a4ae18ba8acd) Add tests for render headers - [`7eff581`](https://github.com/cloudflare/vinext/commit/7eff5814c6f3b57e655bbac62887b5d6f041c951) Review CI failures and plan fixes - [`18f9483`](https://github.com/cloudflare/vinext/commit/18f94833bac6ef05435e4e506fc85ef142d9ed81) Review and fix CI failures - [`d33fdb2`](https://github.com/cloudflare/vinext/commit/d33fdb2455c6230e02b9a31d20176d0cd879c39a) Fix ISR generateRscEntry tests - [`a2cddae`](https://github.com/cloudflare/vinext/commit/a2cddae3e8aa4c5e61f4212bd651d75716c052fe) Fix cached header replay parity - [`36f6ce4`](https://github.com/cloudflare/vinext/commit/36f6ce47b45bce258866db92953ca534a96777dd) Fix cached header replay duplicates - [`17c5808`](https://github.com/cloudflare/vinext/commit/17c5808d6d76d3c9f6744dbabbd872ebfdabf417) Fix cached header replay - [`03ac94d`](https://github.com/cloudflare/vinext/commit/03ac94d469550b0243d729d0934a8caef8b24a8a) Fix render response header parity regressions - [`09b70a5`](https://github.com/cloudflare/vinext/commit/09b70a5e85c1af2ff2ba0fbed21614f501dd4452) Restore app router parity after rebase - [`ca4e4c6`](https://github.com/cloudflare/vinext/commit/ca4e4c67554dfcd2d530239483fe62434dd53fb1) Fix remaining CI failures ### 📊 Changes **41 files changed** (+2976 additions, -825 deletions) <details> <summary>View changed files</summary> 📝 `benchmarks/vinext-rolldown/tsconfig.json` (+2 -1) 📝 `benchmarks/vinext/tsconfig.json` (+2 -1) 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+203 -103) 📝 `packages/vinext/src/index.ts` (+6 -0) 📝 `packages/vinext/src/server/app-page-cache.ts` (+101 -13) 📝 `packages/vinext/src/server/app-page-render.ts` (+32 -3) 📝 `packages/vinext/src/server/app-page-response.ts` (+97 -29) 📝 `packages/vinext/src/server/app-page-stream.ts` (+0 -1) 📝 `packages/vinext/src/server/app-route-handler-execution.ts` (+24 -12) 📝 `packages/vinext/src/server/app-route-handler-response.ts` (+112 -25) 📝 `packages/vinext/src/server/isr-cache.ts` (+2 -1) 📝 `packages/vinext/src/shims/headers.ts` (+196 -19) 📝 `packages/vinext/src/shims/unified-request-context.ts` (+15 -7) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+1179 -569) 📝 `tests/app-page-boundary-render.test.ts` (+2 -2) 📝 `tests/app-page-cache.test.ts` (+68 -2) 📝 `tests/app-page-render.test.ts` (+7 -0) 📝 `tests/app-page-response.test.ts` (+20 -4) 📝 `tests/app-page-stream.test.ts` (+2 -2) 📝 `tests/app-route-handler-execution.test.ts` (+24 -25) _...and 21 more files_ </details> ### 📄 Description Bring cached App Router response-header behavior closer to Next.js by preserving and replaying render-time response headers losslessly. This fixes parity for headers that are mutated during render rather than after the final `Response` is created, including: - `Vary` - `Set-Cookie` - repeated auth challenge headers like `WWW-Authenticate` / `Proxy-Authenticate` - custom response headers written through the shims - headers emitted during async/suspended render work ## What changed - added a render-response-header collector to the request-scoped headers ALS - changed the collector to use a multivalue-safe internal representation instead of native `Headers`, so repeated non-cookie headers are not flattened before caching - persisted cached App Router render headers in a lossless serialized form, preserving repeated values for any header key - replaced cached replay/header application with an explicit merge helper - applied deterministic merge rules across cached render headers, framework headers, and middleware headers: - `set-cookie`: append and preserve order - `vary`: append/merge, never overwrite - `www-authenticate` / `proxy-authenticate`: append repeated values - all other headers: cached render headers are fallback-only; framework and middleware can override - replayed cached render headers on cached `HIT` and `STALE` responses - applied the same merge path to direct RSC `MISS` responses - kept special-response branches aligned so redirects and HTTP fallback responses preserve render-time headers and middleware response headers ## Tests Added and updated coverage for: - HTML `MISS` / `HIT` / `STALE` / post-regeneration `HIT` - direct RSC-first `MISS` / `HIT` - late async render-time header mutation - repeated render-time `Vary` replay across cached responses - repeated `WWW-Authenticate` replay/merge behavior - multi-value `Set-Cookie` replay - middleware override, `Vary`, and cookie merge behavior on cached responses - redirects thrown during metadata resolution - layout-level `notFound()` special-response handling - public `cookies()` and `draftMode()` serialization into render response headers ## Validation Ran: - `pnpm test tests/shims.test.ts tests/isr-cache.test.ts tests/app-router.test.ts tests/entry-templates.test.ts -u` - `pnpm run fmt` - `pnpm run typecheck` ## Notes This does not special-case cookies. The cached artifact is a general render-time response-header payload with lossless multi-value handling for repeated headers. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#610
No description provided.