LinuxでTCP TIME_WAIT・CLOSE_WAITが大量発生した時の調査と対処手順|ssとsysctlで接続状態を把握する

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Linuxtips, Linuxトラブルシューティング > LinuxでTCP TIME_WAIT・CLOSE_WAITが大量発生した時の調査と対処手順|ssとsysctlで接続状態を把握する
「Webサーバーの接続数は増えていないのに、突然 "Cannot assign requested address" エラーが頻発し始めた。」
「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 を設定し、短命接続の繰り返し自体を減らすのが最も効果的


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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

RHEL 9.4 の実行例(トラフィックが多い Web サーバー):

2847 TIME-WAIT 312 ESTABLISHED 48 CLOSE-WAIT 8 LISTEN 1 SYN-SENT

上記の出力では TIME-WAIT が約 2,800 件、CLOSE-WAIT が 48 件ある。2,800 件の TIME-WAIT はやや多いが即座にポート枯渇を起こすレベルではない。一方、CLOSE-WAIT の 48 件はアプリ側の実装問題を示している可能性が高いため、こちらを先に調査すべきだ。

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

デフォルトのポート範囲は 32768 ~ 60999(28,232 ポート)だ。TIME_WAIT が 28,000 件に近づくとポート枯渇が現実的になる。2,847 件ならまだ余裕があるが、アクセスが増加すれば枯渇に達しうる。

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"

デフォルトの 28,232 ポートから 55,535 ポートへ約 2 倍に増加する。TIME_WAIT の蓄積が根本的に解消されるわけではないが、猶予を作りながら根本対処を進める時間を確保できる。

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))

上記の例では PID 12345 の java プロセスが CLOSE_WAIT ソケットを多数保持している。このプロセスのコードを確認して、接続を正しくクローズしていない箇所を特定する。

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接続あたりの最大リクエスト数 }

上流サーバー(PHP-FPM, Gunicorn など)へのプロキシ接続もプールする場合:

# 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

ステップ 2:TIME_WAIT と CLOSE_WAIT どちらが問題かを判断する

・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

PID を特定したら、該当プロセスのコードで 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 で反映
TIME_WAIT は TCP の正常な終了プロセスから発生する避けられない状態だ。問題は「発生している」ことではなく「発生速度がタイムアウト速度を超えて蓄積している」ことにある。まず ss と sysctl で現状を把握し、Keep-Alive 設定の見直しと sysctl チューニングで緩和する。CLOSE_WAIT が増え続ける場合はアプリのコードを見直すことが唯一の根本対処だ。本番環境への sysctl 変更は、設定ファイルへの記述と sysctl --system で確実に永続化しておこう。

ポート枯渇とTCP接続状態を「なぜ」から理解できると、現場での対処速度が変わる

ss コマンドの使い方は調べれば分かります。でも「なぜ TIME_WAIT は 60 秒待つのか」「CLOSE_WAIT が増え続けるとき sysctl では解決できない理由は何か」を現場で即答できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。