Skip to content

fix(middleware): classify Pages data requests by URL - #2039

Merged
james-elicx merged 1 commit into
mainfrom
codex/wave2-middleware-data-requests
Jun 15, 2026
Merged

fix(middleware): classify Pages data requests by URL#2039
james-elicx merged 1 commit into
mainfrom
codex/wave2-middleware-data-requests

Conversation

@james-elicx

Copy link
Copy Markdown
Member

vinext parity: Pages middleware data requests

Date: 2026-06-15
Worktree: /Users/jamesanderson/.codex/worktrees/wave2-middleware-data-requests/vinext
Branch: codex/wave2-middleware-data-requests
Base: origin/main at a3d2f921
Scope: wave2 candidate 2 only; cacheComponents/PPR/resume excluded.

Next.js reference

Ported exact behavior from:

  • test/e2e/middleware-general/test/index.test.ts
    • should not treat as _next/data request with just header
    • should add a rewrite header on data requests for rewrites
    • i18n enabled and disabled matrix
  • test/e2e/middleware-general/app/middleware.js
    • normal redirect behavior
    • /ssr-page rewrite to /ssr-page-2

Reproduction on origin/main

A disposable detached worktree at a3d2f921 ran the header-only redirect assertion against the existing Pages middleware fixture.

Pages Router integration: expected 307, received 200
Production server middleware: expected 307, received 200

The inbound x-nextjs-data: 1 header incorrectly opted an ordinary URL into the data soft-redirect protocol.

Final implementation

  • Dev and Node production classify data requests only after successful build-ID-aware /_next/data/...json URL parsing.
  • The Worker adapter imports a generated normalizeDataRequest() helper and normalizes before runPagesRequest, preserving config redirect/header ordering and stale-build-ID JSON 404 behavior.
  • Cloudflare Vite dev delegates before the host mutates /_next/data URLs, so the Worker receives the original trusted URL.
  • Worker basePath normalization preserves whether the incoming data URL actually carried basePath, including absolute out-of-basePath requests.
  • The shared pipeline emits x-nextjs-rewrite only for trusted real data rewrites.
  • Internal x-middleware-rewrite and forged x-nextjs-data values are not exposed or trusted.

Independent review

Multiple independent codex review --uncommitted passes identified and drove fixes for:

  1. Worker data classification with basePath.
  2. Absolute out-of-basePath data URLs.
  3. Cloudflare Vite-dev handoff after host URL mutation.
  4. Config redirect/header ordering when Worker normalization occurred too late.

Final architecture moves Worker data normalization before the shared request pipeline, resolving all four findings.

Exact validations

Green final gate:

vp check packages/vinext/src/deploy.ts packages/vinext/src/entries/pages-server-entry.ts packages/vinext/src/index.ts packages/vinext/src/server/middleware-runtime.ts packages/vinext/src/server/pages-data-route.ts packages/vinext/src/server/pages-request-pipeline.ts packages/vinext/src/server/prod-server.ts tests/deploy.test.ts tests/entry-templates.test.ts tests/pages-data-route.test.ts tests/pages-request-pipeline.test.ts tests/pages-router.test.ts

vp test run tests/deploy.test.ts -t "keeps Cloudflare dev _next/data URLs intact|runs middleware before routing|does not defer error page rendering for data requests"
# 3 passed

vp test run tests/pages-data-route.test.ts tests/middleware-runtime.test.ts tests/pages-request-pipeline.test.ts
# 83 passed

vp test run tests/pages-router.test.ts -t "does not treat a normal URL as a data request|adds x-nextjs-rewrite for a real data URL"
# 4 passed

vp test run tests/entry-templates.test.ts -t "trusted _next/data classification"
# 1 passed

git diff --check
# clean

Also completed successfully before finalization:

vp run vinext#build

The package build completed with its existing unresolved virtual/private import warnings treated as externals.

@pkg-pr-new

pkg-pr-new Bot commented Jun 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 35bbc72

@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(middleware): classify Pages data requests by URL

I reviewed the diff, traced the dev / Node-prod / Worker request paths, and ran the targeted suites. Verified locally:

  • vp test run tests/pages-data-route.test.ts tests/pages-request-pipeline.test.ts — 71 passed
  • vp test run tests/entry-templates.test.ts tests/deploy.test.ts — 303 passed
  • vp test run tests/pages-router.test.ts -t "does not treat a normal URL...|adds x-nextjs-rewrite for a real data URL" — 4 passed
  • vp check on all 7 changed source files — clean

The core change is sound: classifying _next/data requests by trusted URL normalization instead of the forgeable inbound x-nextjs-data: 1 header. The fix is applied consistently across all three Pages Router entry points (index.ts dev, prod-server.ts, deploy.ts worker), and the reproduction (header-only request producing a spurious soft-redirect) is now covered by ported Next.js tests in both the dev and prod-server integration suites.

I confirmed the basePath-stripping order is consistent in both the Worker (deploy.ts:642-658) and Node prod (prod-server.ts:1672-1704): basePath is stripped before data normalization, so the bare-page normalization is correct and hadBasePath is threaded into wrapMiddlewareWithBasePath as before. The double-normalization in pages-page-handler.ts:303 is a no-op on an already-normalized request (URL is now /page, so isNextDataPathname is false), and asPath resolves to the page path either way — no regression.

Non-blocking observations

1. The new basePath parameter on normalizePagesDataRequest is never exercised by production code.
All three real callers strip basePath upstream and call the helper with the default basePath = "":

  • Worker normalizeDataRequest(request)__normalizePagesDataRequest(request, buildId) (entries/pages-server-entry.ts:236-238)
  • pages-page-handler.ts:303normalizePagesDataRequest(request, buildId)
  • prod-server.ts / index.ts don't use the helper at all (inline parseNextDataPathname)

So the new branches (pages-data-route.ts:204-205, 226-228) are only covered by the unit tests added in tests/pages-data-route.test.ts, not by any integration path. This is harmless but is dead capability in practice — consider either wiring a caller through it or dropping the parameter to avoid an untested code path drifting out of sync.

2. App Router still trusts the x-nextjs-data header for the soft-redirect protocol (app-rsc-handler.ts:1047). This is the same class of issue this PR fixes for Pages Router. It's explicitly out of scope per the PR description (wave2 candidate 2, Pages only), but worth tracking as a follow-up so the App Router path doesn't retain the forgeable classification.

3. The ordering guard in tests/deploy.test.ts is a source-text indexOf assertion. It's brittle by nature, but I confirmed both anchor strings are unique in index.ts, so it reliably enforces the "delegate before mutating _next/data URLs" invariant. Fine as-is.

Overall this looks correct and well-tested for its stated scope. Nothing blocking.

@ask-bonk

ask-bonk Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2039.

Summary

I reviewed PR #2039 (fix(middleware): classify Pages data requests by URL) and posted the review. The PR is correct and well-tested for its stated scope.

What I verified:

  • Ran all targeted test suites locally via vp — all pass (71 + 303 + 4 tests)
  • vp check on all 7 changed source files — clean (format, lint, types)
  • Traced the request path through dev (index.ts), Node prod (prod-server.ts), and Worker (deploy.ts) — the security fix (classify by trusted URL, not the forgeable x-nextjs-data header) is applied consistently in all three
  • Confirmed basePath-stripping order is consistent (stripped before data normalization in both Worker and Node prod)
  • Confirmed the double-normalization in pages-page-handler.ts:303 is a harmless no-op on already-normalized requests

Non-blocking findings I flagged:

  1. The new basePath parameter on normalizePagesDataRequest is never exercised by production code — only by unit tests. All real callers strip basePath upstream. Untested-in-integration capability that could drift.
  2. App Router (app-rsc-handler.ts:1047) still trusts the x-nextjs-data header — same class of issue, explicitly out of scope here, but worth a follow-up.
  3. The tests/deploy.test.ts ordering guard is a brittle source-text indexOf assertion, but the anchor strings are unique so it's reliable.

Nothing blocking.

github run

@james-elicx
james-elicx marked this pull request as ready for review June 15, 2026 10:32
@james-elicx
james-elicx merged commit b58adb3 into main Jun 15, 2026
55 checks passed
@james-elicx
james-elicx deleted the codex/wave2-middleware-data-requests branch June 15, 2026 22:00
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