2026年9月14日
2026年9月14日
WordPressサーバーのTCP設定(tcp_tw_reuse・net.ipv4)を見直してパフォーマンスを改善する
WordPress サーバーの TCP 関連のカーネル設定(net.ipv4.* や net.core.*)は、変えれば速くなるものではなく、TIME_WAIT の大量発生やポートの枯渇、接続待ちキューのあふれといった「詰まり」が確認できたときに効く対策です。この記事では、まず ss や nstat で詰まりの有無を確かめる方法を示し、tcp_tw_reuse をはじめとする主要パラメータの意味、安全な変更と戻し方、そして sysctl より先に試すべき構成の見直しまでを順に説明します。
まず何が詰まっているかを確かめる
設定を変える前に、現状の数字を記録します。後で効果を比べるための基準にもなります。
# 接続状態ごとの概要
ss -s
# TIME_WAIT の数
ss -Htan state time-wait | wc -l
# TIME_WAIT の相手先(最後の列)を多い順に表示
ss -Htan state time-wait | awk '{print $NF}' | sort | uniq -c | sort -rn | head
# 待ち受けソケットのキュー(LISTEN 行の Recv-Q が現在の待ち数、Send-Q が上限)
ss -ltn
# 待ち受けキューのあふれ・破棄の累計
nstat -az TcpExtListenOverflows TcpExtListenDrops
数字と合わせて、ログに次のような記録がないかを見ます。
- Nginx の error.log に
connect() to 127.0.0.1:9000 failed (99: Cannot assign requested address)のような行がある: 送信元ポートが足りず、新しい接続を作れていません。 - Nginx の error.log に
(11: Resource temporarily unavailable) while connecting to upstreamがある: Unix ソケットで PHP-FPM につないでいて、PHP-FPM 側の待ち受けキューがいっぱいです。 dmesgやjournalctl -kにtime wait bucket table overflowがある: TIME_WAIT の数がカーネルの上限に達しています。TcpExtListenOverflowsが負荷の高い時間帯に増えている: 接続の受け付けが追いついていません。
どれにも当てはまらないなら、遅さの原因は TCP ではなく PHP の処理、DB のクエリ、キャッシュ不足にある可能性が高いです。その場合は sysctl を触らず、そちらを先に調べてください。
TIME_WAIT が増える仕組みと WordPress で増える場所
TCP では、先に接続を閉じた側がしばらく TIME_WAIT 状態を保ちます。遅れて届いたパケットが次の接続に紛れ込まないようにするための仕組みで、Linux ではこの時間がおよそ60秒に固定されており、sysctl では変えられません。よく tcp_fin_timeout を短くすると TIME_WAIT が減ると書かれていますが、このパラメータが扱うのは FIN_WAIT_2 状態の時間で、TIME_WAIT とは別物です。
問題になりやすいのは、サーバー自身が「接続しに行く側」になる通信です。WordPress の構成では次のような場所が該当します。
- Nginx から PHP-FPM へ TCP(例:
127.0.0.1:9000)でつないでいる - PHP から MySQL へ
127.0.0.1や別サーバーの IP で TCP 接続している - PHP から Redis や Memcached へ TCP 接続している
- リバースプロキシとして別のバックエンドへ転送している
- プラグインが外部 API へ頻繁に
wp_remote_get()などで接続している
接続しに行く側は、宛先ごとに送信元ポートを1つ使います。既定の送信元ポートの範囲は 32768〜60999 で、約2万8千個です。同じ宛先への接続がそれぞれ60秒間ポートを占有すると、毎秒およそ470件を超える新規接続でポートが足りなくなる計算になります。反対に、訪問者からの接続を受ける側(443番ポート)の TIME_WAIT は送信元ポートを消費しないため、数が多くても通常は問題になりません。
tcp_tw_reuse の意味と設定値
net.ipv4.tcp_tw_reuse は、プロトコル上安全と判断できる場合に、TIME_WAIT のソケットを新しい接続に再利用させる設定です。取れる値は次の3つです。
0: 無効1: すべての接続で有効2: ループバック(127.0.0.1 など)への接続だけ有効
比較的新しいカーネルでは既定値が 2 です。古いカーネルでは 0 が既定の場合があるので、まず現在値を sysctl net.ipv4.tcp_tw_reuse で確かめてください。
押さえておきたいのは次の3点です。
- 効くのは「接続しに行く側」の新規接続だけです。訪問者からの接続を受ける側には影響しません。
- 安全性の判断に TCP タイムスタンプを使うため、
net.ipv4.tcp_timestampsが有効(既定は有効)であることが前提です。 - 既定値が
2なら、Nginx から127.0.0.1の PHP-FPM への接続はすでに対象です。1に変えて意味があるのは、別サーバーの DB やバックエンドへの接続でポート不足が起きている場合です。
カーネルのドキュメントには、この値は専門家の助言なしに変えるべきではないという趣旨の注意があります。1 にするのは、前の章の確認で別ホスト宛てのポート不足が実際に起きているとわかったときに限るのが無難です。
古い記事には tcp_tw_recycle を有効にする設定が載っていることがありますが、これは NAT 配下の利用者の接続を失敗させる問題があり、Linux 4.12 で削除されました。設定ファイルに残っていると適用時にエラーになるので、見つけたら削除してください。
そのほかの主要なパラメータ
net.ipv4.ip_local_port_range
送信元ポートの範囲です。ポート不足が確認できた場合、範囲を広げると同じ宛先へ同時に張れる接続が増えます。
注意点は、範囲内のポートで待ち受けているサービスと衝突しうることです。範囲を広げる前に ss -ltn で待ち受け中のポートを確認し、範囲内にあるものは net.ipv4.ip_local_reserved_ports で予約しておきます。
net.core.somaxconn と tcp_max_syn_backlog
net.core.somaxconn は、アプリケーションが指定できる待ち受けキューの上限です。カーネル 5.4 以降の既定値は 4096 で、それ以前は 128 でした。この値を上げても、アプリケーション側が小さな値を指定していれば効果はありません。Nginx は listen ディレクティブの backlog= パラメータ、PHP-FPM はプール設定の listen.backlog で指定します。PHP-FPM の既定値は PHP のバージョンや OS によって異なるため、設定ファイルで明示しておくと状況を把握しやすくなります。
net.ipv4.tcp_max_syn_backlog は、ハンドシェイク途中の接続を保持する数の上限です。TcpExtListenDrops が増えている場合に、somaxconn と合わせて見直します。
net.ipv4.tcp_max_tw_buckets
TIME_WAIT を同時に保持できる上限です。上限を超えた TIME_WAIT はすぐに破棄されます。カーネルのドキュメントは、この値を人為的に下げるべきではないとしています。time wait bucket table overflow が出ていてメモリに余裕があるなら上げる方向で検討します。
輻輳制御(BBR)と tcp_slow_start_after_idle
訪問者との通信の速さには輻輳制御アルゴリズムも関わります。BBR は回線品質が不安定な環境で効果が出ることがある一方、効果は通信環境によって異なります。sysctl net.ipv4.tcp_available_congestion_control で利用できるアルゴリズムを確認し、試す場合は測定とセットで行ってください。
net.ipv4.tcp_slow_start_after_idle を 0 にすると、しばらく通信がなかった keep-alive 接続でも送信速度の再立ち上げをしなくなります。HTTP/2 のように接続を使い回す通信で多少の効果が見込める程度のもので、優先度は高くありません。
設定を適用する手順と戻し方
本番サーバーに入れる前に、同じ OS・カーネルのステージングで試します。作業の前に現在の値を保存しておきます。
# 現在の値を保存(戻すときの基準)
sudo sysctl -a 2>/dev/null > ~/sysctl-before-$(date +%F).txt
# まず一時的に変更して様子を見る(再起動で元に戻る)
sudo sysctl -w net.ipv4.ip_local_port_range="10240 65535"
問題がなければ、/etc/sysctl.d/ に専用のファイルを作って永続化します。変更する項目は、確認で必要とわかったものだけに絞ります。
# /etc/sysctl.d/99-wordpress-net.conf
# 別ホストの DB への接続でポート不足が確認できたため範囲を拡大
net.ipv4.ip_local_port_range = 10240 65535
# 範囲内で待ち受けているサービスのポートを予約(例)
net.ipv4.ip_local_reserved_ports = 11211
# 待ち受けキューのあふれが確認できたため上限を拡大
net.core.somaxconn = 8192
net.ipv4.tcp_max_syn_backlog = 8192
# 設定ファイルをすべて読み込み直す
sudo sysctl --system
# 反映されたか確認
sysctl net.ipv4.ip_local_port_range net.core.somaxconn
戻すときは、作ったファイルを削除してから、保存しておいた値を sysctl -w で書き戻すか、再起動します。
なお、共用サーバーやマネージドホスティングではカーネル設定を変更できません。コンテナ型の VPS や Docker でも、変更できる項目が限られていたり、コンテナごとに --sysctl で指定する必要があったりします(2026年10月現在。提供事業者やコンテナ基盤の仕様を確認してください)。
sysctl より先に効く構成の見直し
TIME_WAIT やポート不足の多くは、接続を毎回作り直していることが原因です。カーネルの上限を広げるより、接続の数そのものを減らすほうが根本的で、副作用も小さく済みます。
- PHP-FPM への接続を Unix ソケットにする: 同じサーバー内なら
fastcgi_pass unix:/run/php/php-fpm.sock;のように Unix ソケットを使うと、TCP のポートも TIME_WAIT も使いません。ソケットのパスはディストリビューションや PHP のバージョンで異なるので、PHP-FPM のプール設定のlistenと合わせます。 - MySQL への接続を Unix ソケットにする: DB が同じサーバーにあるなら、wp-config.php の
DB_HOSTをlocalhostにすると、PHP の MySQL 拡張は通常 Unix ソケットで接続します。 - バックエンドへの接続を keep-alive で使い回す: Nginx から別サーバーへ転送している場合、
upstreamブロックのkeepaliveと、proxy_http_version 1.1;およびproxy_set_header Connection "";を組み合わせると、接続を毎回作り直さずに済みます。 - ページキャッシュとオブジェクトキャッシュを使う: PHP や DB まで届くリクエスト自体が減るため、内部の接続も減ります。
変更の効果は、同じ条件の負荷テストで比べます。ステージングで変更前と変更後に同じ負荷をかけ、最初の章のコマンドで TIME_WAIT の数、TcpExtListenOverflows の増え方、エラーログの件数、応答時間を記録して比較してください。
まとめ
- TCP のカーネル設定は、TIME_WAIT の大量発生・ポート不足・待ち受けキューのあふれが
ss・nstat・ログで確認できたときの対策 - TIME_WAIT は約60秒で固定され、
tcp_fin_timeoutでは短くならない tcp_tw_reuseは接続しに行く側にだけ効き、新しいカーネルの既定値2でループバックはすでに対象。1は別ホスト宛てのポート不足が確認できた場合に限るtcp_tw_recycleは削除済みで、使わないsomaxconnを上げるなら Nginx のbacklog=や PHP-FPM のlisten.backlogも合わせる- 変更は
/etc/sysctl.d/の専用ファイルで行い、事前に現在値を保存し、ステージングで測定してから本番へ - 先に Unix ソケット化や keep-alive、キャッシュで接続数を減らすほうが根本的