[PR #940] [MERGED] fix(app-router): return 405 for non-action mutations to static pages #966

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/940
Author: @NathanDrake2406
Created: 4/28/2026
Status: Merged
Merged: 4/28/2026
Merged by: @james-elicx

Base: mainHead: nathan/app-page-method-405


📝 Commits (2)

  • 1b9988a fix(app-router): return 405 for non-action mutations to static pages
  • bc96857 fix(app-router): preserve 405 allow header

📊 Changes

5 files changed (+288 additions, -0 deletions)

View changed files

📝 packages/vinext/src/entries/app-rsc-entry.ts (+17 -0)
packages/vinext/src/server/app-page-method.ts (+64 -0)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+96 -0)
tests/app-page-method.test.ts (+97 -0)
📝 tests/app-router.test.ts (+14 -0)

📄 Description

What this changes

Static and SSG App Router page routes now return 405 Method Not Allowed with Allow: GET, HEAD for non-action mutation requests instead of rendering the page.

Why

Next.js has an explicit static/SSG page guard that returns 405 for non-action, non-GET/HEAD page requests: base-server.ts#L2274-L2289.

That guard intentionally excludes possible server action requests, using the same request-shape semantics defined in server-action-request-meta.ts#L26-L43. Next.js also covers the behavior in the prerender suite: prerender.test.ts#L564-L572.

Vinext already handled route handlers and server actions, but a plain POST or PUT to a static App Router page fell through to page rendering.

Approach

  • Add server/app-page-method.ts for the page method policy so the generated RSC entry stays thin.
  • Call that helper before page rendering once route static/SSG metadata is available.
  • Preserve possible server action POSTs and explicit dynamic opt-outs (force-dynamic and revalidate = 0).
  • Merge middleware response headers into the generated 405 response.
  • Add focused helper tests plus a live App Router fixture regression for static and ISR pages.

Validation

  • vp test run tests/app-page-method.test.ts tests/app-router.test.ts tests/entry-templates.test.ts
  • vp check packages/vinext/src/server/app-page-method.ts packages/vinext/src/entries/app-rsc-entry.ts tests/app-page-method.test.ts tests/app-router.test.ts tests/entry-templates.test.ts
  • vp run vinext#build

Risk

The static/SSG classification uses the route metadata currently available in the App Router entry. It preserves explicit dynamic opt-outs and server action requests, and it can become more exact as vinext records richer build-time static classification metadata.


🔄 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/940 **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/app-page-method-405` --- ### 📝 Commits (2) - [`1b9988a`](https://github.com/cloudflare/vinext/commit/1b9988a578d31c5422f4cde4b027737151bc1836) fix(app-router): return 405 for non-action mutations to static pages - [`bc96857`](https://github.com/cloudflare/vinext/commit/bc96857f30ae1682975566321e3106bb3218f588) fix(app-router): preserve 405 allow header ### 📊 Changes **5 files changed** (+288 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+17 -0) ➕ `packages/vinext/src/server/app-page-method.ts` (+64 -0) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+96 -0) ➕ `tests/app-page-method.test.ts` (+97 -0) 📝 `tests/app-router.test.ts` (+14 -0) </details> ### 📄 Description ## What this changes Static and SSG App Router page routes now return `405 Method Not Allowed` with `Allow: GET, HEAD` for non-action mutation requests instead of rendering the page. ## Why Next.js has an explicit static/SSG page guard that returns 405 for non-action, non-GET/HEAD page requests: [base-server.ts#L2274-L2289](https://github.com/vercel/next.js/blob/canary/packages/next/src/server/base-server.ts#L2274-L2289). That guard intentionally excludes possible server action requests, using the same request-shape semantics defined in [server-action-request-meta.ts#L26-L43](https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/server-action-request-meta.ts#L26-L43). Next.js also covers the behavior in the prerender suite: [prerender.test.ts#L564-L572](https://github.com/vercel/next.js/blob/canary/test/e2e/prerender.test.ts#L564-L572). Vinext already handled route handlers and server actions, but a plain `POST` or `PUT` to a static App Router page fell through to page rendering. ## Approach - Add `server/app-page-method.ts` for the page method policy so the generated RSC entry stays thin. - Call that helper before page rendering once route static/SSG metadata is available. - Preserve possible server action POSTs and explicit dynamic opt-outs (`force-dynamic` and `revalidate = 0`). - Merge middleware response headers into the generated 405 response. - Add focused helper tests plus a live App Router fixture regression for static and ISR pages. ## Validation - `vp test run tests/app-page-method.test.ts tests/app-router.test.ts tests/entry-templates.test.ts` - `vp check packages/vinext/src/server/app-page-method.ts packages/vinext/src/entries/app-rsc-entry.ts tests/app-page-method.test.ts tests/app-router.test.ts tests/entry-templates.test.ts` - `vp run vinext#build` ## Risk The static/SSG classification uses the route metadata currently available in the App Router entry. It preserves explicit dynamic opt-outs and server action requests, and it can become more exact as vinext records richer build-time static classification metadata. --- <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#966
No description provided.