Skip to content

fix(pages): preserve gSSP headers on redirects - #2771

Merged
james-elicx merged 3 commits into
mainfrom
codex/fix-pages-gssp-redirect-headers
Aug 3, 2026
Merged

fix(pages): preserve gSSP headers on redirects#2771
james-elicx merged 3 commits into
mainfrom
codex/fix-pages-gssp-redirect-headers

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Summary

  • preserve headers set through context.res when getServerSideProps returns a redirect
  • cover both the Pages dev redirect shortcut and production Fetch-response path
  • enable the pages-router-complex uppercase gallery canonicalization regression

Next.js oracle: packages/next/src/server/render.tsx retains the shared response object while encoding redirect metadata, so headers set before returning the redirect remain on the final response. Verified directly against Next.js 16.2.7 with the same fixture.

Tests

  • vp check --fix packages/vinext/src/server/dev-server.ts packages/vinext/src/server/pages-data-route.ts packages/vinext/src/server/pages-page-data.ts tests/pages-page-data.test.ts tests/e2e/pages-router-complex/gallery-guards.spec.ts
  • vp test run tests/pages-page-data.test.ts
  • vp run vinext#build
  • PLAYWRIGHT_PROJECT=pages-router-complex pnpm run test:e2e -- gallery-guards.spec.ts --reporter=line

@pkg-pr-new

pkg-pr-new Bot commented Jul 31, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2771
npm i https://pkg.pr.new/create-vinext-app@2771
npm i https://pkg.pr.new/@vinext/types@2771
npm i https://pkg.pr.new/vinext@2771

commit: 9685bf7

@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

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 9685bf7 against base 81c0b03 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 134.6 KB 134.6 KB ⚫ -0.0%
Client entry size (gzip) vinext 122.1 KB 122.1 KB ⚫ -0.0%
Dev server cold start vinext 2.99 s 2.96 s ⚫ -0.9%
Production build time vinext 3.28 s 3.29 s ⚫ +0.3%
RSC entry closure size (gzip) vinext 113.8 KB 113.8 KB ⚫ -0.0%
Server bundle size (gzip) vinext 191.3 KB 191.3 KB ⚫ -0.0%

View detailed results and traces

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

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@ask-bonk

ask-bonk Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

@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(pages): preserve gSSP headers on redirects

Reviewed against the Next.js oracle (render.tsx retains the shared response object when encoding redirect metadata). The fix is correct and both the dev shortcut and prod Fetch paths are covered. Tests pass, types/lint clean, and the e2e regression asserts both location and surrogate-control. No blocking issues — a few notes below.

Verified

  • Prod path is clean. PagesResponseStream.resHeaders starts empty and is not seeded from request headers (pages-node-compat.ts:251), so getPagesGsspResponseHeaders(res) returns only headers gSSP set on context.res — exactly the Next.js semantics. Middleware headers are merged separately after renderPage() returns, so there's no double-application.
  • buildNextDataPropsJsonResponse Content-Type is now authoritative. Switching to new Headers(init?.headers) + headers.set("Content-Type", "application/json") means a gSSP-supplied content-type can no longer override the JSON envelope type for _next/data. The old spread order let it win. This is an improvement (the _next/data envelope must be JSON), just noting it's a deliberate behavior change.
  • set-cookie handling stays consistent. getPagesGsspResponseHeaders uses append per array item; for set-cookie that's correct multi-header behavior, and for other arrays append on a fresh Headers is equivalent to the join(", ") used by applyGsspHeaders.
  • README/fixme count is consistent: 13 test.fixme remain in pages-router-complex, matching the "60/73 pass, 13 gaps" update.

Notes / nits (non-blocking)

  1. Dev vs prod header-scope asymmetry. On the normal dev render path, only headers newly set by gSSP are forwarded (via the headersBeforeGSSP diff at dev-server.ts:1188/1294). The new writeGsspRedirect path forwards all headers currently on res (dev-server.ts:330-334), which in dev includes middleware headers already present on res. In practice writeHead would emit those middleware headers anyway (they're already set via setHeader, and the explicit keys override on merge), so this is redundant rather than wrong — but it's a slightly different mental model than the prod path, which only ever sees gSSP-set headers. Worth a one-line comment noting the two paths capture different scopes but converge to the same emitted set, so a future reader doesn't "fix" the perceived inconsistency.

  2. getPagesGsspResponseHeaders duplicates logic in applyGsspHeaders. pages-page-response.ts:461 already maps a PagesGsspResponse into a Headers, with the same set-cookie special-casing. Consider consolidating into one shared helper so the redirect path and the normal render path can't drift (e.g. if someone later adds header filtering to one but not the other). Not required for this PR.

Result

LGTM. Correct, well-tested, and preserves dev/prod parity.

@ask-bonk

ask-bonk Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2771.

Summary

I reviewed PR #2771 (fix(pages): preserve gSSP headers on redirects) by examining the diff, tracing the dev and prod code paths, running the targeted tests, and type/lint checking.

Verdict: LGTM — no blocking issues.

Key verifications:

  • Prod path is correct. PagesResponseStream.resHeaders starts empty and is not seeded from request headers, so getPagesGsspResponseHeaders(res) returns only gSSP-set headers — matching the Next.js oracle semantics.
  • buildNextDataPropsJsonResponse now forces Content-Type: application/json (can no longer be overridden by a gSSP header), which is a correct improvement for _next/data envelopes.
  • set-cookie handling stays consistent with the existing applyGsspHeaders path.
  • Tests pass (2 new parameterized cases), types/lint clean, and the e2e regression asserts both location and surrogate-control. The README fixme count (13 gaps) is consistent.

Non-blocking notes I flagged:

  1. A dev-vs-prod header-scope asymmetry (dev redirect path captures all res headers incl. middleware; prod only sees gSSP-set) — redundant rather than wrong, but worth a clarifying comment.
  2. getPagesGsspResponseHeaders duplicates applyGsspHeaders logic; consider consolidating to prevent future drift.

github run

@james-elicx
james-elicx marked this pull request as ready for review August 3, 2026 15:06
@james-elicx
james-elicx enabled auto-merge (squash) August 3, 2026 15:07
# Conflicts:
#	examples/pages-router-complex/README.md
@james-elicx
james-elicx merged commit 3deb467 into main Aug 3, 2026
66 checks passed
@james-elicx
james-elicx deleted the codex/fix-pages-gssp-redirect-headers branch August 3, 2026 15:43
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