テック技報最新号へ →
コード

Node.js: ALB の裏でたまに 502 が出るなら keepAliveTimeout を ALB のアイドルタイムアウトより長くする

Node の http.Server はアイドルな keep-alive 接続を既定 5 秒で閉じるが、AWS ALB は既定 60 秒まで同じ接続を使い回す。ALB が「まだ使える」と思って送ったリクエストを、Node がちょうど閉じた接続で受けると 502 になる。Node 側を ALB より長くする。

@amitsinha__07 · 投稿 2026-10-20
#nodejs#infra#aws#alb#http#keep-alive

どんな時に気にする?

  • ALB(や他のロードバランサ・リバースプロキシ)の裏で Node(Express / Fastify / Next.js の自前サーバーなど)を動かしている
  • 負荷が低い時間帯に、ランダムで少数の 502 が出る(アプリのログにはリクエストが届いていない)
  • ALB のアクセスログで elb_status_code=502、target_status_code が空(-)

設定例

ts
import http from "node:http";

const server = http.createServer(app);

// ALB のアイドルタイムアウト(既定 60 秒)より長くする
server.keepAliveTimeout = 65_000;
// ヘッダー受信の待ち時間は keepAliveTimeout より長く(古い Node では逆転すると同じ症状が出た)
server.headersTimeout = 66_000;

server.listen(3000);
  • 考え方は「接続を閉じるのは、前に立つ側(ALB)が先」。後ろの Node が先に閉じると、閉じる瞬間と ALB の送信がすれ違う。
  • ALB のアイドルタイムアウトを変えている場合は、その値 + 数秒にする。nginx や Cloudflare など別のプロキシでも同じ考え方(プロキシ側の upstream keep-alive の時間 < Node の keepAliveTimeout)。
  • Express は app.listen() の戻り値が http.Server なので、そこに設定する。

注意点

  • 値を変えても、設定前に張られた接続には効かない(Node のドキュメントにも「接続時に設定される」とある)。起動直後、listen の前後で設定する。
  • Node の最近の版には keepAliveTimeoutBuffer というオプションもあり、実際のソケットのタイムアウトは keepAliveTimeout + keepAliveTimeoutBuffer になる。
  • headersTimeout の既定値は Node 18.14 / 19.4 以降 requestTimeout と 60 秒の小さい方。keepAliveTimeout を 65 秒にするなら、こちらも合わせて上げておくと安全。
  • Go のクライアント側で同じことを考えるなら、Go: 同じホストへ大量に呼ぶなら MaxIdleConnsPerHost を上げる(既定は 2) の「相手の keep-alive より IdleConnTimeout を短く」と同じ理屈(閉じるのは常に相手より先か後かをそろえる)。

出典

X の元の投稿を開く