[PR #939] [MERGED] fix(app-router): reject middleware control responses in route handlers #967

Closed
opened 2026-05-06 13:11:18 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/939
Author: @NathanDrake2406
Created: 4/28/2026
Status: Merged
Merged: 4/28/2026
Merged by: @james-elicx

Base: mainHead: nathan/fix-app-route-nextresponse-helpers


📝 Commits (4)

  • a630fb0 fix(app-router): reject middleware control responses in route handlers
  • 0e03861 Update tests/app-route-handler-execution.test.ts
  • 4f41858 Update packages/vinext/src/server/app-route-handler-response.ts
  • 19ef0e2 fix(app-router): validate route handler ISR regeneration responses

📊 Changes

9 files changed (+219 additions, -0 deletions)

View changed files

📝 packages/vinext/src/server/app-route-handler-cache.ts (+2 -0)
📝 packages/vinext/src/server/app-route-handler-execution.ts (+2 -0)
📝 packages/vinext/src/server/app-route-handler-response.ts (+17 -0)
📝 tests/app-route-handler-cache.test.ts (+61 -0)
📝 tests/app-route-handler-execution.test.ts (+88 -0)
📝 tests/app-route-handler-response.test.ts (+25 -0)
📝 tests/app-router.test.ts (+14 -0)
tests/fixtures/app-basic/app/api/invalid-next-response-next/route.ts (+5 -0)
tests/fixtures/app-basic/app/api/invalid-next-response-rewrite/route.ts (+5 -0)

📄 Description

What this changes

App Route handlers now reject NextResponse.next() and NextResponse.rewrite() responses instead of treating their internal middleware headers as ordinary successful route-handler output.

Why

These helpers are middleware control-flow responses. Next.js rejects them after an App Route handler returns:

Vinext previously accepted those responses and returned 200, which silently diverged from Next.js and could cache or finalize an invalid route-handler response.

Approach

The validation lives in the typed route-handler response helper and is invoked from executeAppRouteHandler() immediately after the user handler returns. That keeps the generated App RSC entry unchanged and prevents invalid middleware-control responses from reaching cache policy, cookie finalization, or response merging.

The check matches Next.js semantics:

  • reject any returned response with x-middleware-rewrite
  • reject returned responses with x-middleware-next: 1
  • allow the existing generic route-handler error path to produce the empty 500 response

Validation

  • vp test run tests/app-route-handler-execution.test.ts tests/app-router.test.ts -t "rejects middleware control responses" failed before the implementation with 200 responses
  • vp fmt
  • vp test run tests/app-route-handler-execution.test.ts tests/app-router.test.ts
  • vp check

Risks / follow-ups

No generated App RSC entry code was added. This intentionally preserves middleware use of NextResponse.next() and NextResponse.rewrite() while rejecting only App Route handler returns.


🔄 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/939 **Author:** [@NathanDrake2406](https://github.com/NathanDrake2406) **Created:** 4/28/2026 **Status:** ✅ Merged **Merged:** 4/28/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `nathan/fix-app-route-nextresponse-helpers` --- ### 📝 Commits (4) - [`a630fb0`](https://github.com/cloudflare/vinext/commit/a630fb0806328987cd19681b290e4a6f34274dfa) fix(app-router): reject middleware control responses in route handlers - [`0e03861`](https://github.com/cloudflare/vinext/commit/0e03861103e9aaa74d2878c4ec322f4387a685e2) Update tests/app-route-handler-execution.test.ts - [`4f41858`](https://github.com/cloudflare/vinext/commit/4f41858d7b933460a36ec66b4a81c3bffcad9531) Update packages/vinext/src/server/app-route-handler-response.ts - [`19ef0e2`](https://github.com/cloudflare/vinext/commit/19ef0e2b237a3b38a2fdde4b805ce3358ee5da15) fix(app-router): validate route handler ISR regeneration responses ### 📊 Changes **9 files changed** (+219 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/server/app-route-handler-cache.ts` (+2 -0) 📝 `packages/vinext/src/server/app-route-handler-execution.ts` (+2 -0) 📝 `packages/vinext/src/server/app-route-handler-response.ts` (+17 -0) 📝 `tests/app-route-handler-cache.test.ts` (+61 -0) 📝 `tests/app-route-handler-execution.test.ts` (+88 -0) 📝 `tests/app-route-handler-response.test.ts` (+25 -0) 📝 `tests/app-router.test.ts` (+14 -0) ➕ `tests/fixtures/app-basic/app/api/invalid-next-response-next/route.ts` (+5 -0) ➕ `tests/fixtures/app-basic/app/api/invalid-next-response-rewrite/route.ts` (+5 -0) </details> ### 📄 Description ## What this changes App Route handlers now reject `NextResponse.next()` and `NextResponse.rewrite()` responses instead of treating their internal middleware headers as ordinary successful route-handler output. ## Why These helpers are middleware control-flow responses. Next.js rejects them after an App Route handler returns: - [`NextResponse.rewrite()` App Route rejection in Next.js](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/packages/next/src/server/route-modules/app-route/module.ts#L968-L972) - [`NextResponse.next()` App Route rejection in Next.js](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/packages/next/src/server/route-modules/app-route/module.ts#L974-L978) - [Next.js e2e coverage for `NextResponse.next()` returning 500](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/test/e2e/app-dir/app-routes/app-custom-routes.test.ts#L557-L575) Vinext previously accepted those responses and returned `200`, which silently diverged from Next.js and could cache or finalize an invalid route-handler response. ## Approach The validation lives in the typed route-handler response helper and is invoked from `executeAppRouteHandler()` immediately after the user handler returns. That keeps the generated App RSC entry unchanged and prevents invalid middleware-control responses from reaching cache policy, cookie finalization, or response merging. The check matches Next.js semantics: - reject any returned response with `x-middleware-rewrite` - reject returned responses with `x-middleware-next: 1` - allow the existing generic route-handler error path to produce the empty `500` response ## Validation - `vp test run tests/app-route-handler-execution.test.ts tests/app-router.test.ts -t "rejects middleware control responses"` failed before the implementation with `200` responses - `vp fmt` - `vp test run tests/app-route-handler-execution.test.ts tests/app-router.test.ts` - `vp check` ## Risks / follow-ups No generated App RSC entry code was added. This intentionally preserves middleware use of `NextResponse.next()` and `NextResponse.rewrite()` while rejecting only App Route handler returns. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:11:18 +02:00
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#967
No description provided.