* fix(middleware): clear nextUrl.basePath for absolute paths outside basePath (part of #1830)
* fix(middleware): reconcile NextURL basePath tests and App Router regression
Address two blocking issues from ask-bonk review on #1872:
1. Update 10 unit tests in tests/shims.test.ts that used basePath-stripped
URLs (e.g. http://localhost/dashboard with basePath="/app") — these tests
encoded the old incorrect behavior where basePath was always set from
config regardless of the URL. Switch them to prefixed input URLs
(http://localhost/app/dashboard) to match the new correct semantics.
2. Fix App Router regression: createNextRequest in middleware-runtime.ts was
receiving normalizedPathname already stripped of basePath (App Router
passes cleanPathname), so the NextRequest URL had no basePath prefix and
_stripBasePath incorrectly cleared basePath to "". Fix by applying
addBasePathToPathname before constructing the URL, mirroring the
un-stripped URL that Next.js's adapter always passes to middleware.
Also use addBasePathToPathname helper in prod-server.ts and deploy.ts
closures instead of duplicating the root-path edge-case logic.
* test(middleware): fix stale test assertions broken by basePath wrapper change
Update 9 tests in deploy.test.ts that checked for the old simple runMiddleware
passthrough ('runMiddleware: typeof runMiddleware === "function" ? runMiddleware : null')
which no longer matches the generated code after the basePath re-add wrapper was
introduced in the deploy adapter.
Fix 1 test in app-route-handler-runtime.test.ts that incorrectly expected NextURL
to re-add the basePath prefix to a URL that was already stripped of it. Per
getNextPathnameInfo semantics (the fix this PR implements for #1830), basePath is
only set when the pathname actually starts with the configured prefix — a stripped
URL stays stripped.
* fix(middleware): gate basePath re-add on in-basePath state and restore route handler URL parity
- Fix the missed import assertion in tests/deploy.test.ts (the generated
entry now imports addBasePathToPathname alongside hasBasePath/stripBasePath).
- Gate the basePath re-add in createNextRequest on a new hadBasePath option:
the unconditional re-add regressed the Pages out-of-basePath flow by
re-prefixing absolute-path requests the adapters deliberately left bare,
making middleware see nextUrl.basePath === "/root" instead of "". The
flag defaults to URL-derived (correct for prod/deploy Pages adapters) and
is asserted true by callers that pass pre-stripped URLs (App Router,
dev server).
- Re-add basePath in createTrackedAppRouteRequest so App Route handlers see
the original prefixed request.url / nextUrl.href and an active
nextUrl.basePath, matching Next.js (the routing layer strips basePath
before handlers run).
- Re-derive the active basePath from the configured value in
NextURL._stripBasePath on every parse, so href reassignment toggles
basePath like Next.js NextURL.analyze().
- Add unit tests covering the App Router nextUrl.basePath re-add, the Pages
in-/out-of-basePath flows, matcher evaluation against stripped paths, and
href re-derivation.
* refactor(middleware): extract shared wrapMiddlewareWithBasePath helper
The runMiddleware basePath re-add closure was duplicated verbatim in
prod-server.ts and the generated worker entry in deploy.ts. Extract it
to wrapMiddlewareWithBasePath in server/pages-request-pipeline.ts (both
adapters already import from that module) to keep the two adapters in
sync.
* test(middleware): add unit coverage for wrapMiddlewareWithBasePath
Covers the helper's gating contract directly: pass-through when
hadBasePath is false or basePath is empty, prefix re-add (preserving
query, headers, ctx, and opts), and idempotent re-add for an
already-prefixed URL.
* fix(middleware): redirect protocol — relative Location and x-nextjs-redirect
Match Next.js's edge adapter behaviour for middleware redirects:
1. Relativize the `Location` header for same-host redirects. Next.js only
emits absolute URLs when the redirect target is cross-origin; vinext
was always emitting `http://host/path` for same-host targets, which
broke deploy-suite parity tests.
2. Translate redirects to the `x-nextjs-redirect` soft-redirect protocol
when the request carries `x-nextjs-data: 1`. The client router uses
this header instead of a raw HTTP 3xx to avoid CORS issues on
cross-origin data fetches.
Both fixes live in `executeMiddleware`, so all four call sites
(prod-server, dev plugin, deploy worker, app-middleware) inherit them.
Refs #1334
* fix(middleware): thread isDataRequest through all callers
Address Bonk review. The previous commit checked `x-nextjs-data` on the
middleware request, but that header is in INTERNAL_HEADERS and is
stripped by `filterInternalHeaders` before any caller constructs the
middleware request -- so the soft-redirect branch was unreachable.
Make `isDataRequest` an explicit option on `ExecuteMiddlewareOptions`
and `ApplyAppMiddlewareOptions`, and capture it from the raw incoming
headers in each of the four entry points (dev plugin, prod server,
deploy worker, App Router RSC handler) before filtering. The generated
Pages Router runMiddleware now accepts a third `options` argument
carrying the flag through to the runtime helper.
Also: drop redundant lowercase `headers.delete("location")` -- the
Fetch spec makes Headers.delete case-insensitive.
Add a regression test ensuring a forged `x-nextjs-data: 1` request
header cannot trigger the soft-redirect path without the caller
explicitly opting in.
Refs #1334