408 Request Timeout
使い方
408 Request Timeout エラーが届いた場合、クライアントはリクエストを開始したものの、何らかの理由でペイロード全体を送信できていません。これはインターネット接続が遅い場合や切断された場合に発生します。レスポンスには、接続が閉じられたことを指定する Connection ヘッダーが含まれます。
Connection: close ヘッダーを受け取ると、クライアントはリクエストを再試行します。
注意
408 Request Timeout は、クライアントがサーバーへリクエストを送るのが遅すぎたことを意味します。504 Gateway Timeout は、プロキシやゲートウェイがバックエンドサーバーの応答を待ってタイムアウトしたことを意味します。408 はクライアントからサーバーへのタイムアウトであり、504 はサーバー間のタイムアウトです。原因は通常一時的であるため、408 は自動再試行が妥当な数少ない 4xx コードの1つです。
SEO への影響
Google のような検索エンジンは、408 ステータスを返す URL をインデックスしません。以前にインデックスされていた URL がこのステータスコードを返すようになると、検索結果から削除されます。
例
クライアントが 10K の PDF ファイルをサーバーへ送信し始めますが、接続が断続的な接続障害に見舞われ、サーバーは送信が遅すぎると判断します。サーバーはリクエストを取り消し、接続を閉じます。接続が安定すると、クライアントはファイルの再送信に成功します。
最初のリクエスト
PUT /docs HTTP/1.1
Host: example.com
Content-Type: application/pdf
Content-Length: 10000
最初のレスポンス
HTTP/1.1 408 Request Timeout
Connection: Close
Content-Type: text/html
Content-Length: 198
<html>
<head>
<title>Connection Close</title>
</head>
<body>
<p>The transmission was not received quickly
enough. Check connectivity and try again.</p>
</body>
</html>
次のリクエスト
PUT /docs HTTP/1.1
Host: example.com
Content-Type: application/pdf
Content-Length: 10000
<File transfer successful for PDF file>
最終レスポンス
HTTP/1.1 200 OK
対処方法
408 Request Timeout は、クライアントが時間内にリクエストを完了しなかったためサーバーが接続を閉じたことを意味します。
ネットワークの接続性とレイテンシを確認します。不安定または遅い接続はリクエストの送信を遅延させます。パケットロスやレイテンシの高いホップを特定するため、サーバーに対して ping と traceroute を実行します。再試行の前に接続が安定していることを確認します。
ペイロードのサイズを削減するか、大きなアップロードを分割します。遅い接続で大きなファイルを送信すると、サーバーの待機限度を超えます。ペイロードを圧縮する、チャンク形式の Transfer-Encoding を使う、またはマルチパートアップロードの API を使ってアップロードを小さなパートに分割します。
クライアント側のタイムアウト設定を引き上げます。HTTP クライアントライブラリやブラウザは独自のタイムアウトを適用します。より遅い転送に対応できるよう、接続と読み取りのタイムアウト値を上げます。curl では —connect-timeout と —max-time を使います。Python の requests では timeout=(connect, read) を設定します。
サーバーのタイムアウト設定を検証します。Web サーバーに応じてタイムアウトのディレクティブを調整します。
nginx:
client_header_timeout 60s;
client_body_timeout 60s;
keepalive_timeout 75s;
send_timeout 60s;
Apache:
Timeout 60
KeepAliveTimeout 5
RequestReadTimeout header=20-40 body=20
RequestReadTimeout ディレクティブ(mod_reqtimeout)は、ヘッダーとボディの読み取りタイムアウトを個別に制御します。クライアントが正当により多くの時間を必要とする場合は、これらの値を増やします。
Keep-Alive の設定を調整します。keepalive_timeout が小さいと、持続的接続でクライアントが次のリクエストを送る前にアイドル接続が閉じられます。nginx では keepalive_timeout のデフォルトは 75 秒です。リクエストがバースト的なクライアントを扱う API では値を上げます。Apache では KeepAliveTimeout(デフォルト 5 秒)を増やし、MaxKeepAliveRequests が1接続あたり十分なリクエスト数を許可していることを検証します。
リバースプロキシとロードバランサーのタイムアウトを確認します。アプリケーションサーバーの前段にリバースプロキシ(nginx、HAProxy、AWS ELB)がある場合、各層が独自のタイムアウトを適用します。プロキシのタイムアウトがバックエンドより短いと、プロキシが先に接続を閉じます。nginx のリバースプロキシモードでは次のようにします。
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
AWS ALB のアイドルタイムアウトはデフォルトで 60 秒であり、ロードバランサーの属性で設定できます。
リクエストを再試行します。408 はしばしば一時的なものです。接続を再確立し、リクエスト全体を再送信します。プログラムから使うクライアントには、指数バックオフを伴う自動再試行ロジックを実装します。Retry-After ヘッダーが存在する場合は、再試行まで待つべき時間を指定します。
サーバーのエラーログを確認します。nginx はエラーログにタイムアウトのイベントを記録します。Apache は mod_reqtimeout の違反を記録します。ヘッダーとボディのどちらの読み取りがタイムアウトしたかを特定するため、/var/log/nginx/error.log または /var/log/apache2/error.log でリクエストのタイムスタンプに一致するエントリを確認します。
コードリファレンス
.NET
HttpStatusCode.RequestTimeout
Rust
http::StatusCode::REQUEST_TIMEOUT
Rails
:request_timeout
Go
http.StatusRequestTimeout
Symfony
Response::HTTP_REQUEST_TIMEOUT
Python3.5+
http.HTTPStatus.REQUEST_TIMEOUT
Java
java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT
Apache HttpComponents Core
org.apache.hc.core5.http.HttpStatus.SC_REQUEST_TIMEOUT
Angular
@angular/common/http/HttpStatusCode.RequestTimeout