「ss コマンドを実行したら TIME_WAIT が 3,000 件を超えていた。これはまずいのか?」
こういったケースのほとんどは、TCP 接続状態の蓄積が原因だ。TIME_WAIT や CLOSE_WAIT は TCP の仕様上、必ず発生する状態だが、設定や実装の問題によって際限なく蓄積する。蓄積が進むとローカルポートが枯渇し、サービス全体への新規接続が失敗する。
この記事では、RHEL 9.4 / Ubuntu 24.04 LTS の実機コマンドを使いながら、TIME_WAIT・CLOSE_WAIT の確認方法・原因の見極め・sysctl とWebサーバー設定による実践的な対処手順を解説する。
この記事のポイント
・TIME_WAIT は正常なTCP終了で発生し、ss -ant | awk '{print $1}' | sort | uniq -c で状態別に集計できる
・TIME_WAIT 大量発生は tcp_tw_reuse 有効化と ip_local_port_range 拡張で緩和できる
・CLOSE_WAIT はアプリ側の close() 漏れが原因で sysctl では根本解決にならない
・Webサーバーの keepalive_timeout を設定し、短命接続の繰り返し自体を減らすのが最も効果的
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
TIME_WAIT・CLOSE_WAIT が大量発生するとどうなるか
Linux の TCP スタックはローカルポート(エフェメラルポート)を使い回しながら接続を管理している。TIME_WAIT・CLOSE_WAIT 状態のソケットが蓄積すると、使用可能なポートが不足し、新規接続時に次のようなエラーが発生する。・
Cannot assign requested address(ポート枯渇時の connect() エラー)・
socket: Too many open files(fd 枯渇が重なった場合)・Nginx や Apache のエラーログに "connect() failed (99: Cannot assign requested address)" が増える
ポート枯渇は深夜のバッチ処理や突発トラフィックで突然顕在化することが多い。定期的に接続状態を確認する習慣がなければ、障害が発生してから初めて気づくケースがほとんどだ。
TCP 接続状態の種類と遷移の仕組み
対処方針を誤らないために、まず TIME_WAIT と CLOSE_WAIT の意味を押さえておく必要がある。TIME_WAIT とは
TCP 接続を正常に終了した側(FIN を先に送った側)が入る待機状態だ。期間は
2 × MSL(Maximum Segment Lifetime) で、Linux ではデフォルト約 60 秒。遅延パケットの誤受信を防ぐために設けられているため、TCP の仕様として不可避だ。HTTP の Keep-Alive を使わない環境や、短命接続を大量に生成するアプリケーションで蓄積しやすい。CLOSE_WAIT とは
相手(クライアントまたはサーバー)から FIN を受け取ったのに、自分側がまだ close() を呼んでいない状態だ。アプリケーションが接続を正常にクローズしていないことを示しており、TIME_WAIT とは性質がまったく異なる。増え続ける CLOSE_WAIT は アプリ側の実装バグ のサインだ。
ss コマンドで TCP 接続状態を確認する
1. 状態別に集計して全体像をつかむ
まず状態ごとの件数を集計して、どの状態が異常なのかを把握する。# 接続状態を集計(LISTEN 除外版) ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
2847 TIME-WAIT 312 ESTABLISHED 48 CLOSE-WAIT 8 LISTEN 1 SYN-SENT
2. ss -s でソケット統計の全体サマリーを確認する
ss -s
Total: 3292 TCP: 3218 (estab 312, closed 2848, orphaned 2, timewait 2847) Transport Total IP IPv6 RAW 0 0 0 UDP 6 4 2 TCP 370 280 90 INET 376 284 92 FRAG 0 0 0
timewait 2847 が TIME_WAIT の総数だ。closed の数とほぼ一致しており、ここが蓄積の主体になっている。3. ローカルポート範囲とポート枯渇を確認する
ポートが実際に枯渇しているかどうかは、エフェメラルポートの範囲と現在の使用数を比較する。Linux ポート確認の全コマンドを参照しながら進めると手順が整理しやすい。# エフェメラルポート範囲の確認 sysctl net.ipv4.ip_local_port_range # 現在使用中のポート数(おおまかな目安) ss -ant | grep -c TIME-WAIT
net.ipv4.ip_local_port_range = 32768 60999 2847
TIME_WAIT が大量発生する原因と対処法
1. 主な原因:短命な TCP 接続の繰り返し
TIME_WAIT の件数が多くなりやすいのは次のケースだ。・HTTP/1.0 またはKeep-Alive 無効の環境(1リクエスト1接続)
・内部 API や DB へのリクエストで接続プールを使っていない
・バッチ処理が短い間隔で大量の TCP 接続を生成・終了している
TIME_WAIT 自体は正常な動作だが、生成速度が消化速度(約 60 秒のタイムアウト)を超えると蓄積する。
2. tcp_tw_reuse を有効にして TIME_WAIT ソケットを再利用する
net.ipv4.tcp_tw_reuse を有効にすると、1 秒以上前の TIME_WAIT ソケットを接続元として再利用できるようになる。接続を開始する側(クライアント側)のサーバー、つまりバックエンドへ接続するフロントエンドサーバーで有効にする。# 現在の設定を確認 sysctl net.ipv4.tcp_tw_reuse # 即時に有効化(再起動で消える) sysctl -w net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_reuse = 0 net.ipv4.tcp_tw_reuse = 1
tcp_tw_recycle(Linux 4.12 で廃止)は NAT 環境で接続障害を引き起こす危険なパラメータだ。現行カーネルには存在しないが、古い資料を参考にする際は必ず確認すること。tcp_tw_reuse は NAT 環境でも安全に使用できる。3. tcp_fin_timeout を短縮して FIN_WAIT2 の滞在を減らす
net.ipv4.tcp_fin_timeout は FIN_WAIT2 状態の維持時間だ。デフォルトは 60 秒で、これを短縮することで TIME_WAIT に移行するまでの時間を短くし、ソケットの解放を早める。# 確認 sysctl net.ipv4.tcp_fin_timeout # 30秒に短縮 sysctl -w net.ipv4.tcp_fin_timeout=30
4. ip_local_port_range を拡張して使用可能ポートを増やす
ポート枯渇が差し迫っている場合は、エフェメラルポートの範囲を広げる。# 1024~65535 まで広げる(well-known ポートの 1024 以下は除く) sysctl -w net.ipv4.ip_local_port_range="10000 65535"
CLOSE_WAIT が大量発生する原因と対処法
1. CLOSE_WAIT の原因:アプリ側の close() 漏れ
CLOSE_WAIT はクライアントから FIN を受け取ったのに、アプリが close() を呼ばない状態だ。TIME_WAIT とは根本原因がまったく異なる。よくある原因パターン:
・接続オブジェクトの try/finally ブロックでのクローズ漏れ(Java, Python のコードバグ)
・コネクションプール実装のバグ(接続がプールに戻されず放置)
・スレッドが例外終了した際に接続がクローズされずに残る
CLOSE_WAIT は sysctl のパラメータ変更では解消しない。アプリを再起動すれば一時的に減るが、根本対処にはコード修正が必要だ。
2. CLOSE_WAIT を起こしているプロセスを特定する
# CLOSE_WAIT の件数を確認 ss -ant state CLOSE-WAIT | wc -l # CLOSE_WAIT を起こしているプロセス名とPIDを確認(root権限推奨) ss -antp state CLOSE-WAIT
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process CLOSE-WAIT 0 0 192.168.1.10:8080 203.0.113.25:54321 users:(("java",pid=12345,fd=23)) CLOSE-WAIT 0 0 192.168.1.10:8080 203.0.113.30:48921 users:(("java",pid=12345,fd=31)) CLOSE-WAIT 0 0 192.168.1.10:8080 203.0.113.42:52017 users:(("java",pid=12345,fd=45))
3. CLOSE_WAIT の件数増加を監視して問題の深刻度を判断する
CLOSE_WAIT は一定数で安定していれば問題ない場合もある。継続して増加しているかを確認する。# 5秒おきに CLOSE_WAIT の件数を確認 watch -n 5 "ss -ant state CLOSE-WAIT | wc -l"
sysctl パラメータで設定を永続化する
sysctl -w による変更は再起動で消える。本番環境への適用は /etc/sysctl.d/ に設定ファイルを置いて永続化する。# /etc/sysctl.d/99-tcp-tuning.conf を作成 cat > /etc/sysctl.d/99-tcp-tuning.conf << 'EOF' # TIME_WAIT ソケットの再利用(接続開始側のサーバーに適用) net.ipv4.tcp_tw_reuse = 1 # FIN_WAIT2 の維持時間を短縮(デフォルト60秒→30秒) net.ipv4.tcp_fin_timeout = 30 # エフェメラルポートの範囲を拡張 net.ipv4.ip_local_port_range = 10000 65535 EOF # 設定を即時反映 sysctl --system
* Applying /usr/lib/sysctl.d/00-system.conf ... * Applying /etc/sysctl.d/99-tcp-tuning.conf ... net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30 net.ipv4.ip_local_port_range = 10000 65535 * Applying /etc/sysctl.conf ...
tcp_tw_reuse はサーバーが接続を開始する側(バックエンドへリクエストするフロントエンド、DB クライアントとして動作するアプリサーバーなど)に適用する。純粋に接続を受け付けるだけのサーバー(DB サーバー本体、Listen 専用サービス)への適用効果は限定的だ。WebサーバーのKeep-Alive設定でTIME_WAITを抑制する
TIME_WAIT の根本的な抑制策は、短命な TCP 接続の生成を減らすことだ。HTTP Keep-Alive を正しく設定すれば、1 つの TCP 接続で複数のリクエストを処理でき、接続の生成・終了の回数そのものが減少する。1. Nginx の keepalive_timeout 設定
# /etc/nginx/nginx.conf(http コンテキスト) http { keepalive_timeout 65; # クライアント接続の Keep-Alive 維持時間(秒) keepalive_requests 1000; # 1接続あたりの最大リクエスト数 }
# upstream ブロックで接続プールを有効化 upstream backend { server 127.0.0.1:8000; keepalive 32; # アイドル状態で維持するコネクション数 } server { location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; # Keep-Alive ヘッダーをクリア } }
2. Apache の KeepAlive 設定
# /etc/httpd/conf/httpd.conf または conf.d/keepalive.conf KeepAlive On MaxKeepAliveRequests 1000 KeepAliveTimeout 5
KeepAliveTimeout を長くしすぎると ESTABLISHED 接続が増加してメモリを消費するため、5 ~ 15 秒程度が目安だ。トラブルシュートの切り分けフロー
TIME_WAIT・CLOSE_WAIT の問題に直面したときの確認手順をまとめる。ステップ 1:現在の状態を確認する
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn ss -s sysctl net.ipv4.ip_local_port_range
・TIME_WAIT が多い → sysctl 調整と Webサーバー keepalive 設定を見直す
・CLOSE_WAIT が増え続ける → アプリの接続クローズ実装を調査する
・両方多い → それぞれを独立して対処する
ステップ 3:ポート枯渇の有無を確認する
# 使用中ポート数とポート範囲を比較 ss -ant | grep -cE 'TIME-WAIT|CLOSE-WAIT' sysctl net.ipv4.ip_local_port_range
ip_local_port_range を拡張して猶予を確保しながら根本対処を進める。ステップ 4:CLOSE_WAIT の場合はプロセスを特定してコードを確認する
ss -antp state CLOSE-WAIT
connection.close() / try-with-resources / with 構文など、接続のクローズが確実に行われているかを確認する。本記事のまとめ
| 状態 | 主な原因 | 対処法 |
|---|---|---|
| TIME_WAIT 大量蓄積 | 短命接続の繰り返し(Keep-Alive 未設定) | tcp_tw_reuse 有効化・ip_local_port_range 拡張・keepalive_timeout 設定 |
| CLOSE_WAIT が増え続ける | アプリ側の close() 漏れ(コードバグ) | アプリの接続クローズ実装を修正(sysctl は根本解決にならない) |
| ポート枯渇(Cannot assign requested address) | TIME_WAIT/CLOSE_WAIT 蓄積によるポート不足 | ip_local_port_range の即時拡張で猶予を確保してから根本対処 |
| 設定の永続化 | /etc/sysctl.d/ に反映されていない | /etc/sysctl.d/99-tcp-tuning.conf に記述して sysctl --system で反映 |
sysctl --system で確実に永続化しておこう。
ポート枯渇とTCP接続状態を「なぜ」から理解できると、現場での対処速度が変わる
ss コマンドの使い方は調べれば分かります。でも「なぜ TIME_WAIT は 60 秒待つのか」「CLOSE_WAIT が増え続けるとき sysctl では解決できない理由は何か」を現場で即答できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます