Repository navigation
fix(control-plane): pin next to 16.2.5 to fix standalone i18n redirect loop (#1926) - #1928
Merged
Merged
Conversation
…t loop (#1926) next 16.2.6 regressed how the standalone server resolves next-intl locale rewrites. With the standalone default HOSTNAME=0.0.0.0, the i18n rewrite is emitted as an absolute localhost URL and treated as cross-origin, so every page route returns a 307 to itself (ERR_TOO_MANY_REDIRECTS). Bisected: 16.2.5 serves 200 with a relative rewrite; 16.2.6 and 16.2.7 loop. next dev is unaffected. Pin next to 16.2.5 (exact) and add a root override so next-intl's peer dedupes to the same single version — a 16.2.5/16.2.6 split fails the control-plane typecheck. The Docker image build resolves the exact pin; CI `npm ci` installs the pinned lockfile (single hoisted next@16.2.5, all platform binaries kept). Temporary: 16.2.6 is a security release, so we should return to a patched version once the regression is fixed upstream. Tracking: vercel/next.js#94342.
nicoloboschi
added a commit
that referenced
this pull request
Jun 2, 2026
) Reverts the temporary `next` pin from #1928. Deeper investigation showed the control-plane redirect loop (#1926) is NOT a 16.2.6 regression: it reproduces identically on 16.2.5 and 16.2.6, and is triggered specifically by binding the standalone server to HOSTNAME=127.0.0.1 (Next normalizes 127.0.0.1 -> localhost in the proxy request URL but keeps 127.0.0.1 in the router's initUrl, so the next-intl locale rewrite looks cross-origin and leaks as a 307 loop). The production launchers (docker start-all.sh, bin/cli.js) bind HOSTNAME=0.0.0.0, which serves 200 on every version, so the pin neither fixed #1926's repro nor was needed for production. Restoring ^16.2.6 brings back the 16.2.6 security fixes (proxy-bypass + SSRF). The 127.0.0.1-binding quirk is unrelated to the version. Verified: npm ci -> single next@16.2.7; control-plane build typechecks; standalone on HOSTNAME=0.0.0.0 serves /login, /banks/*, /es/login as 200.
nicoloboschi
added a commit
that referenced
this pull request
Sep 15, 2026
) * fix(control-plane): serve the UI when bound to a loopback address (#1926) A literal loopback HINDSIGHT_CP_HOSTNAME makes the control plane 307 every page to itself. Next.js standalone uses HOSTNAME both to bind and as the origin its next-intl locale rewrite is checked against; Next normalizes 127.0.0.1 -> localhost on one side of that comparison but keeps the literal on the other, so the rewrite reads as cross-origin and leaks out instead of being applied internally. What makes it expensive is that nothing else notices. The bind is correct, /health answers, the whole REST API works — only the browser surface is dead, so container health checks and smoke tests stay green while the UI is completely unreachable. Bind as `localhost` instead when a loopback literal is requested, and pin Node's DNS result order to the family that was asked for. The pin is not incidental: `localhost` resolves through /etc/hosts, and Docker's generated hosts file maps `::1 localhost`, so substituting the name alone would move a requested IPv4 bind onto IPv6. With the pin the effective bind is unchanged and only the spelling differs, which is the whole point — the operator gets the address they asked for and a UI that loads. Measured on 0.10.0 (Next 16.3.5), control-plane-only containers, one variable at a time: HINDSIGHT_CP_HOSTNAME bind GET /dashboard 127.0.0.1 127.0.0.1 307 -> itself (loop) ::1 ::1 500 localhost ::1 200 <- family changed localhost + ipv4first 127.0.0.1 200 localhost + ipv6first ::1 200 Verified end to end by mounting the patched script over /app/start-all.sh in the published 0.10.0 image: 127.0.0.1 and ::1 both now bind their own family and return 200, and 0.0.0.0 is untouched and silent. Upgrading Next does not fix this. #1928 pinned 16.2.5 for this issue and #1934 reverted the pin after re-diagnosing, concluding production was unaffected — which holds for the 0.0.0.0 default and not for a deliberately loopback-only deployment — and deferred the loopback case to a follow-up. That follow-up has not landed; 16.3.5 still loops. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(control-plane): apply the loopback bind fix to the npm CLI too bin/cli.js passed HOSTNAME / -H 127.0.0.1 straight to the standalone server, so npx users binding to loopback still got the #1926 redirect loop. Mirror resolve_cp_hostname: bind as localhost and pin --dns-result-order to the requested family, appending to any existing NODE_OPTIONS. Verified in the 0.10.0 image: 127.0.0.1 and ::1 now serve /dashboard 200 on the same family. --------- Co-authored-by: snevesgraca-nwmo <257406126+snevesgraca-nwmo@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
cdbartholomew
added a commit
that referenced
this pull request
Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #1926 — every control-plane page route 307-redirect-loops (
ERR_TOO_MANY_REDIRECTS) under the standalone production server.Root cause: a Next.js regression in 16.2.6. With the standalone default
HOSTNAME=0.0.0.0, next-intl'slocalePrefix: "as-needed"locale rewrite is emitted as an absolutelocalhostURL and resolved as cross-origin, so the rewrite leaks to the client as a 307 to the original path → loop.Bisected (raw standalone,
HOSTNAME=0.0.0.0, identical CP code):next devis not affected — only the standalone build, which is why it surfaced only when building/shipping the production image. This is also why 0.7.1 images that resolved 16.2.5 worked.Change
nextto16.2.5(exact) inhindsight-control-plane.overrides.next: "16.2.5"so next-intl's peer dedupes to the same single version (a 16.2.5/16.2.6 split fails the CP typecheck with aNextRequestmismatch).16.2.6 → 16.2.5, single hoisted, all platform binaries preserved).16.2.6 is a security release (multiple high-severity middleware/proxy bypass fixes + an SSRF). Pinning to 16.2.5 re-opens those, so this should be treated as a stopgap until the regression is fixed upstream and we can move back to a patched version.
Upstream issue filed for guidance: vercel/next.js#94342
Verification
npm ciat root → single dedupednext@16.2.5, consistent lockfile.npm run build --workspace=hindsight-control-plane→ compiles + typechecks.HOSTNAME=0.0.0.0:/login,/banks/abc,/es/login→ 200 (was 307 loop).