Skip to content

fix: apply middleware headers to intercept route and server action responses#762

Merged
james-elicx merged 3 commits intocloudflare:mainfrom
NathanDrake2406:fix/middleware-headers-intercept-action
Apr 3, 2026
Merged

fix: apply middleware headers to intercept route and server action responses#762
james-elicx merged 3 commits intocloudflare:mainfrom
NathanDrake2406:fix/middleware-headers-intercept-action

Conversation

@NathanDrake2406
Copy link
Copy Markdown
Contributor

Summary

  • Middleware response headers (_mwCtx.headers) were silently dropped on three code paths: intercepting route responses, server action re-render responses, and server action redirect responses
  • All three constructed Response objects directly without merging middleware headers, unlike the normal page path which goes through buildAppPageRscResponse()
  • This caused auth cookies, CORS headers, and security headers set in middleware to be missing from these response types
  • Extracted mergeMiddlewareResponseHeaders() from buildAppPageRscResponse() and applied it to all three paths, plus refactored the original caller to use the shared helper

Test plan

  • Added unit tests for mergeMiddlewareResponseHeaders helper (null-safe, Set-Cookie/Vary append, other headers set)
  • Added integration test verifying middleware headers appear on intercept route RSC responses
  • Added generated-code assertions verifying mergeMiddlewareResponseHeaders is called in intercept, action re-render, and action redirect paths
  • Existing app-router tests pass (279/279)
  • Existing features tests pass (264/264)
  • Entry template snapshots updated

…sponses

Middleware response headers (_mwCtx.headers) were silently dropped on three
code paths: intercepting route responses, server action re-render responses,
and server action redirect responses. All three constructed Response objects
directly without merging middleware headers, unlike the normal page path
which goes through buildAppPageRscResponse().

Extract mergeMiddlewareResponseHeaders() from the inline logic in
buildAppPageRscResponse() and call it in all three paths. This ensures
auth cookies, CORS headers, and security headers set in middleware are
applied consistently to all response types.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 3, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@762

commit: c89252a

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90271239d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"x-action-redirect-type": actionRedirect.type,
"x-action-redirect-status": String(actionRedirect.status),
});
__mergeMiddlewareResponseHeaders(redirectHeaders, _mwCtx.headers);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve server-action redirect control headers

In the action-redirect branch, the code builds x-action-redirect* headers and then merges middleware headers afterward; because mergeMiddlewareResponseHeaders() uses set() for most keys, middleware can overwrite these control headers. If a middleware sets x-action-redirect, x-action-redirect-type, or x-action-redirect-status (intentionally or via header passthrough), the client can navigate to the wrong URL or fail to process the redirect correctly. Merge middleware headers before writing the action redirect control headers, or explicitly protect these keys from override.

Useful? React with 👍 / 👎.

Codex review correctly identified that mergeMiddlewareResponseHeaders()
uses set() for most keys, meaning middleware headers could overwrite the
x-action-redirect* control headers if a middleware happened to set them.

Fix by merging middleware headers first, then writing the x-action-redirect*
headers afterward so they are always authoritative. The framework's own
redirect control signals must survive regardless of what middleware sets.
@james-elicx
Copy link
Copy Markdown
Collaborator

/bigbonk review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good fix. The three code paths (intercept route, action re-render, action redirect) were silently dropping middleware headers — auth cookies and security headers vanishing is a real production problem, and this is the right fix.

The extraction of mergeMiddlewareResponseHeaders() is clean, the merge ordering in the redirect path correctly protects x-action-redirect* control headers (addressed in d0bf61a), and the test coverage is solid (unit tests on the helper, integration test on intercept, generated-code assertions on all three paths).

Two minor observations:

  1. Pre-existing HTML/RSC parity gap: buildAppPageHtmlResponse (line 241) still uses append for all middleware headers, while buildAppPageRscResponse now delegates to the extracted helper with set()-for-singular / append-for-Set-Cookie-and-Vary. This means middleware setting Cache-Control will override on the RSC path but append (producing an invalid multi-value) on the HTML path. Not introduced by this PR, but worth a follow-up to align buildAppPageHtmlResponse with mergeMiddlewareResponseHeaders too.

  2. Minor: Headers iterator keys are already lowercase per spec, so key.toLowerCase() on line 179 is technically redundant — but it makes intent clear, so fine to keep.

return;
}

for (const [key, value] of middlewareHeaders) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Headers iterator always yields lowercase keys per the Fetch spec, so key.toLowerCase() here is a no-op. Harmless and defensive — just noting for awareness.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 3, 2026

Review submitted and approved. Here's a summary of the review:

Approved — This is a solid bug fix addressing middleware headers being silently dropped on three code paths: intercepting route responses, server action re-render responses, and server action redirect responses.

Key findings:

  • The mergeMiddlewareResponseHeaders() extraction is clean and correctly shared across all affected paths
  • The merge ordering in the redirect path correctly protects x-action-redirect* control headers by merging middleware first, then set()-ing the framework headers (addressed in commit d0bf61a after the Codex review)
  • The redirect path intentionally keeps status: 200 hardcoded (not _mwCtx.status ?? 200) since it's a special protocol response — correct choice
  • Test coverage is comprehensive: unit tests, integration test, and generated-code assertions

Two minor observations noted in the review:

  1. Pre-existing parity gap: buildAppPageHtmlResponse still uses append for all middleware headers while the RSC path now uses the smarter set()/append split — worth a follow-up
  2. Trivial: key.toLowerCase() is technically redundant since Headers iterators yield lowercase keys per spec, but it's harmless and communicates intent

github run

@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

5 approved in a row. God damn that hit the spot

@james-elicx james-elicx merged commit 1e2e68f into cloudflare:main Apr 3, 2026
24 checks passed
@NathanDrake2406 NathanDrake2406 deleted the fix/middleware-headers-intercept-action branch April 3, 2026 11:46
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.

2 participants