nextjs - ✅(Solved) Fix compound pageExtensions with node-runtime proxy can emit server/middleware.js but skip standalone copy [1 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#91600Fetched 2026-04-08 00:57:50
View on GitHub
Comments
2
Participants
2
Timeline
10
Reactions
0
Timeline (top)
labeled ×4commented ×2referenced ×2cross-referenced ×1

Root Cause

  • proxy.api.js is accepted as the root proxy file because it matches the proxy detection regex built from pageExtensions.
  • With runtime = "nodejs", the build emits .next/server/middleware.js and .next/server/middleware.js.nft.json.
  • functions-config-manifest.json remains empty.
  • middleware-manifest.json remains empty.
  • .next/standalone/.../.next/server/middleware.js is missing.

Fix Action

Fixed

PR fix notes

PR #91736: fix(build): use basename regexp to detect proxy/middleware with compound pageExtensions

Description (problem / solution / changelog)

<!-- Thanks for opening a PR! -->

What?

Fix proxy/middleware detection in the production build and dev watcher when compound are used (e.g. ).

Why?

When a root proxy file is named proxy.api.js, path.parse().name returns 'proxy.api' (the full stem up to the final extension). The subsequent equality check fileBaseName === PROXY_FILENAME (where PROXY_FILENAME = 'proxy') never matches, so proxyFilePath is never set.

Consequence:

  • functions-config-manifest.json is not written with /_middleware
  • middleware.js is not copied into the standalone output
  • Downstream tooling that expects the node middleware artifact in standalone breaks

The same bug existed in both the production build path (packages/next/src/build/index.ts) and the dev watcher (server/lib/router-utils/setup-dev-bundler.ts).

How?

Both files already build detection regexps from pageExtensions (e.g. ^proxy\.(?:api\.js|page\.tsx)$). Replace the plain fileBaseName === PROXY_FILENAME equality checks with detectionRegExp.test(path.basename(filePath)). This correctly matches proxy.api.js regardless of how many extension segments are present.

Fixes #91600

Changed files

  • packages/next/src/build/index.ts (modified, +14/-4)
  • packages/next/src/server/lib/router-utils/setup-dev-bundler.ts (modified, +20/-3)

Code Example

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.3.0: Wed Jan 28 20:48:41 PST 2026; root:xnu-12377.81.4~5/RELEASE_ARM64_T6041
  Available memory (MB): 49152
  Available CPU cores: 14
Binaries:
  Node: 22.21.1
  npm: 10.9.4
  Yarn: N/A
  pnpm: 10.13.1
Relevant Packages:
  next: 16.2.0-canary.104
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: N/A
Next.js Config:
  output: standalone
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/relsunkaev/next-proxy-pageextensions-repro

To Reproduce

  1. Clone the repro.
  2. Run pnpm install.
  3. Keep export const runtime = "nodejs" in proxy.api.js.
  4. Run NODE_ENV=production CI=1 pnpm build.
  5. Inspect these files:
    • .next/server/middleware.js
    • .next/server/middleware.js.nft.json
    • .next/server/functions-config-manifest.json
    • .next/standalone/**/.next/server/middleware.js
  6. Observe that the build emits server/middleware.js, but does not write /_middleware into functions-config-manifest.json and does not copy middleware.js into standalone output.

Current vs. Expected behavior

Current behavior:

  • proxy.api.js is accepted as the root proxy file because it matches the proxy detection regex built from pageExtensions.
  • With runtime = "nodejs", the build emits .next/server/middleware.js and .next/server/middleware.js.nft.json.
  • functions-config-manifest.json remains empty.
  • middleware-manifest.json remains empty.
  • .next/standalone/.../.next/server/middleware.js is missing.

Expected behavior:

  • If proxy.api.js is accepted as a valid root proxy file, Next should classify it as middleware consistently.
  • In the node runtime case, functions-config-manifest.json should contain /_middleware.
  • middleware.js should be copied into standalone output.

Why this seems to happen:

  • Detection regex is built from pageExtensions, so proxy.api.js matches.
  • Later classification uses path.parse(rootPath).name === "proxy".
  • For proxy.api.js, path.parse(...).name is "proxy.api", so proxyFilePath is never set.
  • That leaves hasNodeMiddleware false, so standalone copy never runs.

Important scope note:

  • I could not reproduce the same failure without runtime = "nodejs".
  • Without that export, the same repro is classified as edge middleware instead, and the missing server/middleware.js standalone artifact is no longer the relevant failure mode.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.3.0: Wed Jan 28 20:48:41 PST 2026; root:xnu-12377.81.4~5/RELEASE_ARM64_T6041
  Available memory (MB): 49152
  Available CPU cores: 14
Binaries:
  Node: 22.21.1
  npm: 10.9.4
  Yarn: N/A
  pnpm: 10.13.1
Relevant Packages:
  next: 16.2.0-canary.104
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: N/A
Next.js Config:
  output: standalone

Which area(s) are affected? (Select all that apply)

  • Middleware
  • Output
  • Pages Router
  • Turbopack

Which stage(s) are affected? (Select all that apply)

  • next build (local)
  • Other (Deployed)

Additional context

This came from a real app using compound page extensions:

  • pageExtensions: ["page.tsx", "api.ts"]
  • root proxy file named proxy.api.ts
  • export const runtime = "nodejs"

In that app, Next emits .next/server/middleware.js, but standalone output is missing middleware.js, which then breaks downstream tooling that expects standalone to contain the node middleware artifact.

I verified the repro on the latest canary (16.2.0-canary.104).

extent analysis

Fix Plan

To resolve the issue, we need to modify the next.config.js file to correctly handle the proxy.api.js file as a middleware file.

Here are the steps:

  • Update the next.config.js file to include a custom middleware function that checks for the proxy.api.js file.
  • Modify the pageExtensions to include the .api.js extension.

Code Changes

// next.config.js
module.exports = {
  // ... other configurations ...
  experimental: {
    // ... other experimental configurations ...
    middleware: [(req, res, next) => {
      if (req.url === '/_middleware') {
        // Handle the middleware request
      }
      next();
    }],
  },
  pageExtensions: ['page.tsx', 'api.ts', 'api.js'], // Add .api.js extension
};

Verification

To verify the fix, run the following commands:

  1. pnpm build
  2. Check the generated files in the .next directory to ensure that middleware.js is present in the standalone output.
  3. Verify that the functions-config-manifest.json file contains the /_middleware entry.

Extra Tips

  • Make sure to update the next version to the latest canary version (16.2.0-canary.104 or later) to ensure that the fix is applied.
  • If you are using a custom middleware function, ensure that it is correctly handling the proxy.api.js file.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING