Skip to content

Conversation

@Rezakarimzadeh98
Copy link

Description

Fixes #38863

This PR ensures that next dev returns 405 Method Not Allowed for non-GET/HEAD HTTP requests to page routes, making the behavior consistent with next start.

Changes

  • Added HTTP method validation in router-server.ts for page routes
  • Non-GET/HEAD requests (POST, PUT, DELETE, PATCH, etc.) to pages now return 405
  • API routes continue to accept all HTTP methods
  • Added Allow: GET, HEAD header in 405 responses
  • Added comprehensive integration tests

Before

In development mode, POST requests to pages returned 200 OK:

curl -X POST http://localhost:3000/ 
# Returns 200 OK (incorrect)

Copilot AI review requested due to automatic review settings November 22, 2025 12:53
@ijjk
Copy link
Member

ijjk commented Nov 22, 2025

Allow CI Workflow Run

  • approve CI run for commit: 668ec3d

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes issue #38863 by ensuring that non-GET/HEAD HTTP requests to Pages Router page routes return 405 Method Not Allowed in development mode, making the behavior consistent with production mode. The fix adds HTTP method validation in the dev server's request router while preserving the existing behavior for API routes which accept all HTTP methods.

Key Changes

  • Added HTTP method validation for Pages Router page routes in development mode
  • Non-GET/HEAD requests to pages now return 405 with appropriate Allow: GET, HEAD header
  • API routes continue to accept all HTTP methods as expected
  • Added comprehensive test coverage for various HTTP methods on both pages and API routes

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/next/src/server/lib/router-server.ts Adds HTTP method validation for Pages Router pages, returning 405 for non-GET/HEAD requests while excluding API routes
packages/next/src/server/base-server.ts Reformats long comments to span multiple lines for better readability
test/development/pages-dir/http-methods/http-methods.test.ts Adds comprehensive tests for HTTP method validation on pages and API routes
test/development/pages-dir/http-methods/pages/index.js Test fixture for root page
test/development/pages-dir/http-methods/pages/about.js Test fixture for nested page
test/development/pages-dir/http-methods/pages/api/hello.js Test fixture for API route to verify all methods are still allowed
.gitignore Adds local reproduction files (should be removed)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Reza Karim Zadeh added 2 commits November 22, 2025 17:04
- Added logic in router-server.ts to check HTTP method for page routes
- Non-GET/HEAD requests to pages now return 405 Method Not Allowed
- API routes continue to accept all HTTP methods
- Added comprehensive integration tests

Fixes vercel#38863
@Rezakarimzadeh98 Rezakarimzadeh98 force-pushed the fix/issue-38863-post-request-405 branch from 8cffd26 to fc12a25 Compare November 22, 2025 13:34
Comment on lines +17 to +18
const getPageExtensions =
require('../../../../packages/eslint-plugin-next/src/utils/url').getPageExtensions
Copy link
Contributor

Choose a reason for hiding this comment

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

The test file attempts to import a function getPageExtensions from packages/eslint-plugin-next/src/utils/url that doesn't exist in that module.

View Details
📝 Patch Details
diff --git a/packages/eslint-plugin-next/src/utils/url.ts b/packages/eslint-plugin-next/src/utils/url.ts
index c0d7c22bdd..9f71a84934 100644
--- a/packages/eslint-plugin-next/src/utils/url.ts
+++ b/packages/eslint-plugin-next/src/utils/url.ts
@@ -190,6 +190,61 @@ export function execOnce<TArgs extends any[], TResult>(
   }
 }
 
+/**
+ * Gets the configured page extensions from next.config.js or next.config.mjs.
+ * Falls back to default extensions if not configured.
+ * Result is memoized.
+ */
+export const getPageExtensions = execOnce(() => {
+  const defaultExtensions = ['tsx', 'ts', 'jsx', 'js']
+  
+  try {
+    const configPath = path.resolve(process.cwd(), 'next.config.js')
+    const msjConfigPath = path.resolve(process.cwd(), 'next.config.mjs')
+    
+    let config: any = null
+    let configExists = false
+
+    // Try to load next.config.js first
+    if (fs.existsSync(configPath)) {
+      configExists = true
+      try {
+        config = require(configPath)
+      } catch {
+        // If require fails, continue without config
+      }
+    }
+
+    // If next.config.js doesn't exist or failed to load, try next.config.mjs
+    if (!configExists && fs.existsSync(msjConfigPath)) {
+      try {
+        // For ESM files, we need to handle this differently
+        // In a test environment, jest.doMock will handle this
+        // In runtime, dynamic import would be needed
+        config = require(msjConfigPath)
+        if (config.default) {
+          config = config.default
+        }
+      } catch {
+        // If loading fails, use default
+      }
+    }
+
+    // Extract pageExtensions from config
+    if (config && Array.isArray(config.pageExtensions) && config.pageExtensions.length > 0) {
+      const extensions = config.pageExtensions.map((ext: string) => {
+        // Remove leading dot if present
+        return ext.startsWith('.') ? ext.slice(1) : ext
+      })
+      return extensions
+    }
+  } catch {
+    // If anything goes wrong, return default
+  }
+
+  return defaultExtensions
+})
+
 function ensureLeadingSlash(route: string) {
   return route.startsWith('/') ? route : `/${route}`
 }

Analysis

Missing getPageExtensions export in eslint-plugin-next/src/utils/url.ts

What fails: Test file test/unit/eslint-plugin-next/utils/url.test.ts attempts to import getPageExtensions from packages/eslint-plugin-next/src/utils/url, but this function was not exported from the module. When running tests, all 8 test cases fail with a runtime error trying to access undefined property.

How to reproduce:

pnpm test -- test/unit/eslint-plugin-next/utils/url.test.ts

Error (before fix):

Cannot read property 'getPageExtensions' of undefined

Verified with direct module inspection:

const m = require('./packages/eslint-plugin-next/src/utils/url');
console.log(m.getPageExtensions); // undefined

What was implemented: Added getPageExtensions export to packages/eslint-plugin-next/src/utils/url.ts. This function:

  • Returns default page extensions: ['tsx', 'ts', 'jsx', 'js']
  • Reads custom extensions from next.config.js or next.config.mjs
  • Removes leading dots from configured extensions
  • Falls back to defaults if:
    • No config file exists
    • pageExtensions is not an array
    • pageExtensions array is empty
    • Config file fails to load
  • Result is memoized using the existing execOnce utility to ensure function calls return the same reference (per test requirement)

Verification: All 8 test cases in test/unit/eslint-plugin-next/utils/url.test.ts pass:

  • ✓ returns default extensions when next.config.js is not found
  • ✓ returns custom extensions from next.config.js
  • ✓ falls back to default when pageExtensions is not an array
  • ✓ removes leading dots from extensions
  • ✓ returns default when pageExtensions is empty array
  • ✓ supports next.config.mjs with ES module export
  • ✓ handles custom extensions like .page.tsx and .mdx
  • ✓ memoizes the result on subsequent calls

Full eslint-plugin-next test suite: 23 test suites, 189 tests all pass

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

POST request succeeds for pages with next dev

2 participants