Angular SSR redirects can take healthy proxies upstreams out of rotation through an unbounded Location header
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
- #34366 @SkyZeroZx による — マージされずにクローズ
評価
- 難易度
- 3/5
- 見積もり時間
- 半日
- 初心者へのやさしさ
- 30/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- angular, nginx, nodejs, typescript
- 領域
- backend
調査の方向性
Start with createRedirectResponse() in the Angular SSR package, where resHeaders.set("Location", location) sets the header without a length check, and with the render-redirect path from #31497 that sends the Router's final URL. Linked PR #34366 is already open against this issue, so read it first. Reproduce with the lab repo linked in the issue, using a long query string. Done means the response no longer produces an oversized Location for such requests, with a test covering it.
索引モデルが issue の本文から書いたものです。
説明
Description
createRedirectResponse() sets Location from request data without any length limit:
resHeaders.set("Location", location);
Route redirects copy request path segments into a relative redirectTo, and render redirects send the Router's final URL, query included, when it differs from the request (#31497). Both are intended, but nothing bounds the size. The header is at least as large as the request, and percent-encoding on the way makes it bigger: new URL() encodes " ' < > and the Router's query serializer encodes characters like / = ? |, so it is about 3x a raw request, and about 5x when a guard puts state.url in returnUrl and the % is encoded again. Both encodings are correct.
Behind nginx this affects other users. The response doesn't fit in nginx's default 4k proxy_buffer_size, so nginx answers 502 and counts it as a failed attempt for that backend (max_fails=1, fail_timeout=10s). With two backends and one worker, two unauthenticated GETs leave the upstream empty and every request gets 502 for about ten seconds, while both Node processes are fine.
I think this is bit similar as unpoly-rails GHSA-m875-3xf6-mf78.
A buffer sized for the app's largest redirect avoids it, but nothing tells the operator that size and it changes when someone adds a guard: for the plain returnUrl guard at nginx's 8 KB request line it is about 40 KB. max_fails=0 also avoids it, by turning passive health checks off.
The app doesn't need any redirect. With the server routes on RenderMode.Server, as in the SSR guide, this request gets a 302 with a 4,208 B Location:
GET /home/(x)?q=<1,400 raw "> 1,412 B
The ** route drops the unmatched (x) group, so the Router ends on /home?q=.... In the browser that is a replaceState; on the server #31497 turns it into a 302. Nothing in the Router needs to change.
Minimal Reproduction
// app.routes.ts
export const routes: Routes = [
{ path: "", component: Home },
{ path: "home", component: Home },
{ path: "**", component: NotFound },
];
// app.routes.server.ts
export const serverRoutes: ServerRoute[] = [
{ path: "**", renderMode: RenderMode.Server },
];
ng build
NG_ALLOWED_HOSTS=localhost PORT=4000 node dist/app/server/server.mjs &
NG_ALLOWED_HOSTS=localhost PORT=4001 node dist/app/server/server.mjs &
nginx with one worker and nothing else tuned:
worker_processes 1;
events {}
http {
upstream ssr {
server 127.0.0.1:4000;
server 127.0.0.1:4001;
}
server {
listen 8080;
location / {
proxy_pass http://ssr;
proxy_set_header Host localhost;
}
}
}
The request has to go out raw, fetch and node:http percent-encode the quotes first. I sent one per second for 25 s while normal traffic hit /home, and counted until /home answered 200 again:
| Setup | /home OK |
|---|---|
| nginx, the config above | 15.2% |
| nginx, Compose service with two replicas | 9.5% |
| nginx image as shipped on 2 vCPUs (two workers) | 25.2% |
nginx, worker_processes auto with a shared zone |
9.3% |
OpenResty, plain upstream |
4.8% |
Kong 3.7, passive checks with 502 in unhealthy.http_statuses |
1.3% |
| nginx, one backend | 100% |
| Same setups without those requests | 100% |
I also reproduced it on a VPS with nginx behind Cloudflare.
See https://github.com/SkyZeroZx/angular-ssr-location-lab
Exception or Error
[error] upstream sent too big header while reading response header from upstream, request: "GET /home/(x)?q=..."
[warn] upstream server temporarily disabled while reading response header from upstream
[error] no live upstreams while connecting to upstream, request: "GET /home HTTP/1.1"
Your Environment
Angular CLI 22.2.2
@angular/ssr 22.2.2
@angular/core 22.2.2
@angular/router 22.2.2
Node.js 22.23.3
Build production
Engine AngularNodeAppEngine
Render mode RenderMode.Server
nginx 1.27.5, 1.29.3, 1.30.4 and 1.31.2
- 主要言語
- TypeScript
- スター
- 27k
- フォーク
- 11.8k
- 平均マージ
- 1日 3時間
- マージ済み PR(30日)
- 193
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
angular/angular-cli のほかの issue
-
area: @angular/cli
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
angular/angular-cli#34307 ·
メンテナーはふだん 1 日以内に返信
-
area: @angular/build gemini-triaged
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
angular/angular-cli#33955 ·
メンテナーはふだん 1 日以内に返信
-
area: @angular/cli gemini-triaged
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
angular/angular-cli#33055 · コメント 1 件 · リアクション 3 件 ·
メンテナーはふだん 1 日以内に返信
-
@angular/build: lazy routes get no SSR modulepreload hints when the app's tsconfig is not at the workspace root (ɵentryName relative to basePath, chunk map relative to workspace root)対応中かも @alan-agius4 が 1 日前に担当しました。 オープンarea: @angular/build area: @angular/ssr
難易度 4/5 3〜5日 初心者へのやさしさ 25/100
angular/angular-cli#34369 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
Chunk optimizer drops a chunk shared between the main bundle and a web worker対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンarea: @angular/build
難易度 4/5 3〜5日 初心者へのやさしさ 25/100
angular/angular-cli#34292 ·
メンテナーはふだん 1 日以内に返信
angular/angular-cli の issue をすべて見る
似ている issue
-
area:security documentation effort:S priority:P2
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
メンテナーはふだん 1 日以内に返信
-
app:web meta:ops
難易度 2/5 半日 初心者へのやさしさ 78/100
osu-tournament-rating/otr#1113 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
typegoose/mongodb-memory-server#1040 ·
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 92/100