Skip to content

fix(app-router): honor basePath false rewrites#2361

Open
james-elicx wants to merge 2 commits into
mainfrom
codex/fix-app-basepath-false-rewrites
Open

fix(app-router): honor basePath false rewrites#2361
james-elicx wants to merge 2 commits into
mainfrom
codex/fix-app-basepath-false-rewrites

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Summary

  • allow App Router requests outside basePath to reach config rules that explicitly opt out with basePath: false
  • keep App filesystem routes, metadata routes, image handling, server actions, and not-found rendering inaccessible outside the configured prefix unless middleware or a rewrite maps the request into framework routing
  • preserve original basePath scope when matching redirects and rewrites

Next.js parity

Fixes the remaining failure in:

  • test/e2e/app-dir/app-basepath/index.test.ts
    • should redirect externally when encountering absolute URLs on the same host outside the basePath

Reference: https://github.com/vercel/next.js/blob/v16.2.6/test/e2e/app-dir/app-basepath/index.test.ts

Validation

  • vp test run tests/app-rsc-request-normalization.test.ts tests/app-rsc-handler.test.ts --maxWorkers=1 — 148 passed
  • focused vp check for all four changed files
  • exact Next.js v16.2.6 deploy suite with one worker — 13 passed, 0 failed

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@pkg-pr-new

pkg-pr-new Bot commented Jun 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2361
npm i https://pkg.pr.new/vinext@2361

commit: 7fc2a61

@github-actions

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
workers-cache preview production
web preview production

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

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.

Review: fix(app-router): honor basePath false rewrites

Reviewed the diff against Next.js v16.2.6 behavior. The approach is sound and well-targeted. Verified locally:

  • tsc --noEmit clean for the vinext package
  • tests/app-rsc-request-normalization.test.ts + tests/app-rsc-handler.test.ts — 148 passed
  • tests/app-router-external-rewrite.test.ts + tests/app-router-next-config-dev.test.ts — 21 passed
  • Added an ad-hoc test confirming the afterFiles basePath: false shape (the actual Next.js fixture shape) also works out-of-basePath — passes.

What looks correct

  • normalizeRscRequest's new options.allowOutOfBasePath defaults to {}, so every existing caller (and the 50+ tests that call it without options) keeps the strict 404 behavior. Only the App Router handler opts in. Good backward-compat.
  • canUseFilesystemRoutes() as a re-evaluated closure over didMiddlewareRewrite / configRewriteFired is the right design — later checks correctly observe the updated state after middleware/rewrites flip the flags.
  • The redirectDestinationWithBasePath(..., hadBasePath) gate is correct: with the new basePathState, matchRedirect only matches basePath: true rules for in-basePath requests, so out-of-basePath redirects are only ever basePath: false rules and must not be re-prefixed.
  • The removed TODO(#1333) and stale basePathState = { ..., hadBasePath: true } comment are appropriately deleted now that the issue is resolved.
  • stripBasePath is a no-op for non-prefixed paths, so the refactored normalization branch (else if (hadBasePath)) preserves the old /__vinext/ behavior; setting hadBasePath = true for /__vinext/ paths is correct since they are internal endpoints.

Non-blocking observations

  1. Test coverage gap vs. the real fixture. The Next.js fixture (test/e2e/app-dir/app-basepath/next.config.js) returns a flat array from rewrites(), which Next.js maps to afterFiles — but the added unit test only exercises beforeFiles. The afterFiles path does work (I verified), but since that's the exact shape the cited Next.js test relies on, consider adding an afterFiles (and ideally fallback) variant of the test so a future regression in the post-match rewrite loops is caught. The two loops set configRewriteFired = true independently from the beforeFiles loop.

  2. Pages/App parity divergence (pre-existing, worth tracking). pages-request-pipeline.ts:502 rejects out-of-basePath requests with a 404 before the afterFiles/fallback loops run (gated only on !configRewriteFired, which beforeFiles sets). This PR makes App Router honor basePath: false rewrites in afterFiles/fallback too, which is the more Next.js-correct behavior — but it means a basePath: false afterFiles/fallback rewrite now behaves differently between Pages Router and App Router. Per AGENTS.md's dev/prod + Pages/App parity guidance, this is worth a follow-up issue to bring the Pages pipeline in line (or a deliberate, documented note on why they differ).

Neither observation blocks merge. LGTM.

@ask-bonk

ask-bonk Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2361.

Summary

The PR is well-scoped and correct. It resolves issue #1333 by letting out-of-basePath App Router requests reach config rules that explicitly opt out via basePath: false, while keeping framework-owned routing (filesystem routes, metadata, images, server actions, not-found) gated behind canUseFilesystemRoutes().

Verified:

  • Typecheck clean; the PR's tests (148) and related rewrite/config tests (21) pass.
  • The afterFiles basePath: false shape — which is what the cited Next.js v16.2.6 fixture actually uses (flat-array rewrites() → afterFiles) — also works out-of-basePath (confirmed with an ad-hoc test).
  • Backward compatibility preserved: the new allowOutOfBasePath option defaults to off, so all other callers keep strict 404 behavior.

Two non-blocking observations raised in the review:

  1. The added unit test only covers beforeFiles, but the real Next.js fixture uses afterFiles. Suggested adding afterFiles/fallback variants to guard the post-match rewrite loops.
  2. A Pages-vs-App parity divergence: pages-request-pipeline.ts:502 rejects out-of-basePath requests before afterFiles/fallback run, so this PR makes App Router honor those rewrites where Pages Router does not — worth a tracked follow-up per AGENTS.md parity guidance.

No blockers — LGTM.

github run

@github-actions

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 7fc2a61 against base de014d4 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 117.1 KB 117.1 KB ⚫ +0.0%
Client entry size (gzip) vinext 112.5 KB 112.5 KB ⚫ +0.0%
Dev server cold start vinext 2.75 s 2.78 s ⚫ +1.2%
Production build time vinext 3.27 s 3.25 s ⚫ -0.5%
RSC entry closure size (gzip) vinext 93.0 KB 93.1 KB ⚫ +0.1%
Server bundle size (gzip) vinext 157.0 KB 157.0 KB ⚫ +0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@james-elicx james-elicx marked this pull request as ready for review June 25, 2026 18:36
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.

1 participant