【テクニカル・上級編】 HTTPのKeep-AliveタイムアウトとTCP接続の切断タイミング – ネットワーク基礎とWebセキュリティ実践ガイド

TCPの「終わりの美学」:Keep-Aliveタイムアウトが握るパフォーマンスとセキュリティの境界線

ネットワークエンジニアとして現場を渡り歩いていると、往々にして「接続は繋げばいい」という単純な思考がシステムの首を絞める光景に出くわす。特に、Webの入り口であるHTTPの Keep-Alive と、その土台となるTCP層の切断タイミングの設計は、まさに「攻め」と「守り」のバランスを体現する職人芸だ。

今回は、単なる設定値のチューニングを超え、パケットがワイヤー上を駆け巡る挙動から、現代のインフラアーキテクチャが直面するリソース消費の真実までを紐解いていこう。

—

1. パケットの断末魔:FIN/ACKとRSTの峻別

HTTPの Keep-Alive が有効なとき、サーバーとクライアントは同一のTCPコネクションを再利用し、ハンドシェイクのオーバーヘッドを削減する。だが、タイムアウトに達したとき、何が起きるか。

サーバーサイドで Keep-Alive タイムアウト(例えばApacheの KeepAliveTimeout やNginxの keepalive_timeout)が過ぎると、サーバーは FIN パケットを送り、Gracefulな終了を試みる。ここで重要なのは、「サーバーが先に切断を仕掛ける」という点だ。

もし高負荷時にこのタイムアウトが長すぎれば、TIME_WAIT 状態のソケットが枯渇し、いわゆる「接続待ち行列」が溢れ出す。逆に短すぎれば、頻繁な接続再確立(SYN/SYN-ACK/ACK)が発生し、特にTLSハンドシェイクが絡む現代のWebでは、CPU負荷が指数関数的に増大する。

現場で見る「負の連鎖」

TLS 1.3が普及したとはいえ、鍵交換の計算コストは依然として無視できない。TCPの Keep-Alive タイムアウトを極端に短く設定すると、クライアントは接続が切れるたびに再度TLSハンドシェイクを要求することになる。これにより、ネットワークのRTT(Round Trip Time)が積み重なり、ユーザー体験(UX)は著しく低下する。

—

2. カーネルレベルでのチューニング:TCPバッファとコネクション管理

アプリケーション層のチューニングだけでは、現代の高速ネットワークには追いつかない。特にLinuxカーネルの sysctl パラメータは、パケットの「呼吸」を左右する。

# TCPの接続維持に関連するカーネルパラメータの最適化
# TIME_WAIT状態のソケットを再利用可能にする(短命な接続が多いWebサーバーで有効)
sysctl -w net.ipv4.tcp_tw_reuse=1

# TCPの送受信バッファを動的に調整し、スループットを最大化する
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"

# 接続のタイムアウトを短縮し、ゾンビ接続を排除する
sysctl -w net.ipv4.tcp_fin_timeout=15

これらの設定は、サーバーのメモリ消費とセッション維持能力のトレードオフを最適化する。特に tcp_tw_reuse は、頻繁な接続切断が発生する環境下でのリソース枯渇を防ぐ最後の砦だ。

—

3. セキュリティの視点:Slowloris攻撃とヘッダー圧縮の罠

セキュリティスペシャリストとして見逃せないのが、Keep-Alive を悪用したDoS攻撃だ。攻撃者は Keep-Alive を維持したまま、極めて遅い速度でヘッダーを送信し続ける(Slowloris攻撃)。これにより、サーバー側のワーカースレッドやソケットを占有し、正当なユーザーの接続を拒否する。

この回避策として、Nginxなどのリバースプロキシでは以下の設定が必須となる。

# 接続維持時間を適切に設定(長すぎるとSlowlorisの餌食になる)
keepalive_timeout 65;

# クライアントからのヘッダー送信タイムアウトを厳格に制限
client_header_timeout 10;
client_body_timeout 10;

# 一つのコネクションで処理できるリクエスト数に上限を設ける
keepalive_requests 1000;

keepalive_requests を設定することは非常に重要だ。これを無制限にすると、一つのTCPコネクションが恒久的に保持され、ロードバランサーの負荷分散が偏る(Stickyな状態になる)リスクが生じる。

—

4. 結び:アーキテクトが目指すべき「最適解」

究極のパフォーマンスを追求するならば、「パケットを飛ばさないこと」が最大の最適化である。

  • HTTP/2, HTTP/3 (QUIC) への移行: HTTP/2の多重化やQUICのコネクションマイグレーションを活用することで、従来のTCP接続管理の制約から脱却できる。
  • TLS Session Resumption: TLSのハンドシェイクを省略するセッション再開技術を積極的に導入せよ。
  • モニタリング: netstat や ss コマンドで、定常的に TIME_WAIT や ESTABLISHED の推移を監視し、ベースラインを把握しておくこと。

ネットワークのチューニングに「万能の正解」はない。しかし、パケットがどのように生成され、どのように消えていくのかという「通信のライフサイクル」を深く理解していれば、目の前のトラフィックが何を訴えているのか、必ず聞こえるようになるはずだ。

技術は常に進化するが、TCP/IPが抱える物理的制約という真理は変わらない。さあ、次は君のサーバーのパケットログを覗いてみる番だ。そこには、まだ誰も最適化していない「無駄」が隠れているかもしれない。

コメント

タイトルとURLをコピーしました