2026年9月8日
2026年9月8日
Nginxのタイムアウト設定|504・408の原因の見分け方とPHP-FPMとの合わせ方
Nginx のタイムアウトは「クライアントとの間」と「上流(PHP-FPM やバックエンド)との間」の2系統に分かれています。504 Gateway Time-out の多くは、後者の fastcgi_read_timeout や proxy_read_timeout と、PHP 側の制限が噛み合っていないことで起きます。この記事では、主なタイムアウト項目の意味と既定値、エラーログから原因の項目を読み取る方法、WordPress で長い処理だけに余裕を持たせる設定の仕方を説明します。
タイムアウトの全体像
Nginx を前に置いた WordPress では、1つのリクエストが次の区間を通ります。
- ブラウザ → (CDN・ロードバランサー)→ Nginx
- Nginx → PHP-FPM(または別サーバーへのリバースプロキシ)
- PHP-FPM → PHP スクリプトの実行 → データベースや外部 API
それぞれの区間に別々のタイムアウトがあり、いちばん短いものが先に切れます。Nginx の値だけを伸ばしても、PHP-FPM や CDN が先に切ってしまえば症状は変わりません。値を変える前に、どの区間で切れているのかを特定するのが最初の作業です。
主なタイムアウト項目と既定値
以下は Nginx 公式ドキュメントに記載された既定値です(2026年10月時点)。お使いのバージョンのドキュメントもあわせて確認してください。
クライアント側の項目:
| 項目 | 既定値 | 意味 |
|---|---|---|
client_header_timeout |
60s | リクエストヘッダーを受け取りきるまでの待ち時間。超えると 408 |
client_body_timeout |
60s | リクエスト本文の連続する2回の読み取りの間隔の上限。アップロード全体の時間ではない |
send_timeout |
60s | 応答を送るとき、連続する2回の書き込みの間隔の上限 |
keepalive_timeout |
75s | 持続的接続を開けたまま待つ時間 |
上流側の項目(FastCGI の場合。リバースプロキシなら proxy_ で始まる同名の項目):
| 項目 | 既定値 | 意味 |
|---|---|---|
fastcgi_connect_timeout |
60s | PHP-FPM への接続確立までの時間。通常 75 秒を超えて設定できない |
fastcgi_send_timeout |
60s | PHP-FPM へリクエストを送る際の書き込み間隔の上限 |
fastcgi_read_timeout |
60s | PHP-FPM からの読み取り間隔の上限。処理が長い PHP はここで 504 になる |
「間隔」の上限である項目が多いのが Nginx の特徴です。たとえば fastcgi_read_timeout 60s は「応答全体を60秒以内に返せ」ではなく、「60秒間何も届かなければ切る」という意味です。
エラーログから原因の項目を特定する
504 や 408 が出たら、まずエラーログを確認します。
sudo tail -n 50 /var/log/nginx/error.log
sudo grep -i "timed out" /var/log/nginx/error.log | tail -n 20
よく見るメッセージと、対応する項目は次のとおりです。
| エラーログの文言 | 原因の区間・項目 |
|---|---|
upstream timed out (110: Connection timed out) while reading response header from upstream |
PHP-FPM が応答を返す前に fastcgi_read_timeout(または proxy_read_timeout)を超えた。最も多いパターン |
upstream timed out ... while connecting to upstream |
接続自体に時間がかかっている。PHP-FPM のプロセスが埋まっているか、バックエンドが止まっている |
client timed out ... while reading client request headers(または body) |
クライアント側。回線が遅い、または低速攻撃の可能性 |
upstream prematurely closed connection |
Nginx ではなく PHP-FPM 側が先に処理を打ち切った。502 になることが多く、PHP 側の制限を見直す |
アクセスログに $request_time や $upstream_response_time を記録していれば、どの URL が何秒かかっているかも分かります。特定の管理画面の操作だけが遅いのか、全体が遅いのかで対処が変わります。
PHP側の制限とNginxの順番をそろえる
PHP の実行時間を縛る設定は主に2つあります。
max_execution_time(php.ini):スクリプトの実行時間の上限です。PHP マニュアルによると、Linux などではシステムコールやストリーム操作、データベースへの問い合わせにかかった時間は含まれないため、実際の経過時間はこの値より長くなることがあります。WordPress のインポーターなど、set_time_limit()で実行中に値を変えるコードもあります。request_terminate_timeout(PHP-FPM のプール設定):経過時間でワーカーを強制終了する上限です。既定は 0(無効)です。プール単位の設定なので、同じプールを使うすべてのリクエストに同じ値がかかります。
おすすめは、同じリクエストについて PHP 側の上限を Nginx の読み取りタイムアウトより短くすることです(request_terminate_timeout < fastcgi_read_timeout)。PHP が先に止まれば、PHP-FPM のログに記録されて原因を追いやすくなります。逆に Nginx が先に切ると、PHP はそのまま処理を続けているのに利用者には 504 が返り、利用者が再実行して二重送信になるなどの問題が起きやすくなります。
たとえば通常のページを fastcgi_read_timeout 60s で運用するなら、PHP-FPM のプールは次のように 60 秒より短くします。
; /etc/php/8.3/fpm/pool.d/www.conf(パスはディストリビューションと PHP のバージョンで異なります)
request_terminate_timeout = 55s
PHP-FPM の設定を変えたら、sudo systemctl reload php8.3-fpm のようにサービス名を合わせて再読み込みします。
WordPressでは長い処理の場所だけ伸ばす
サイト全体の fastcgi_read_timeout を何百秒にも伸ばすと、遅いリクエストが PHP-FPM のプロセスを長時間占有し、混雑時にほかのページまで遅くなります。長い処理が必要なのは、多くの場合インポートやバックアップ、管理画面の一部の操作だけです。
ただし、上で説明したとおり request_terminate_timeout はプール単位です。Nginx 側で管理画面だけ fastcgi_read_timeout を延ばしても、PHP-FPM が 55 秒で打ち切れば延長は効きません。管理画面と表側で上限を分けたい場合は、管理画面用に別のプールを作り、Nginx からそちらへ振り分けます。
管理画面用のPHP-FPMプールを作る
Debian・Ubuntu で PHP 8.3 を使う場合の例です。既存の www.conf と同じディレクトリに新しいファイルを作ります。
; /etc/php/8.3/fpm/pool.d/wp-admin.conf
[wp-admin]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm-wp-admin.sock
listen.owner = www-data
listen.group = www-data
pm = ondemand
pm.max_children = 4
pm.process_idle_timeout = 30s
; Nginx の fastcgi_read_timeout(180s)より短くする
request_terminate_timeout = 170s
php_admin_value[max_execution_time] = 170
user・group・listen.owner は既存の www.conf と同じ値にそろえます。pm.max_children は同時に動く長い処理の数の上限です。サーバーのメモリに合わせて小さめにしておくと、重い処理が並んでもメモリを使い切りにくくなります。
# 設定の構文チェック(バイナリ名は PHP のバージョンに合わせる)
sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm
ls -l /run/php/php8.3-fpm-wp-admin.sock
Nginxで管理画面だけ別プールへ送る
# 管理画面配下の PHP(具体的なほうを先に書く)
location ~ ^/wp-admin/.+\.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm-wp-admin.sock;
fastcgi_read_timeout 180s;
}
# 通常の PHP
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 60s;
}
snippets/fastcgi-php.conf は Debian・Ubuntu のパッケージに含まれるファイルです。ほかの環境では fastcgi_params を読み込み、SCRIPT_FILENAME を自分で指定します。ソケットのパスも PHP のバージョンや環境で変わるので、既存の設定に合わせてください。
正規表現の location は書かれた順に評価され、最初に一致したものが使われます。順番を逆にすると /wp-admin/ のリクエストも通常の PHP 側に一致してしまい、延長が効きません。なお admin-ajax.php も /wp-admin/ 配下なので、表側から呼ばれる Ajax も管理画面用のプールで処理されます。表側の Ajax が多いサイトでは、pm.max_children が足りているかを確認してください。
これで、表側は「PHP 55 秒 < Nginx 60 秒」、管理画面は「PHP 170 秒 < Nginx 180 秒」と、どちらも PHP が先に止まる順番になります。
何分もかかる処理はブラウザーから実行しない
そもそも何分もかかる処理は、ブラウザー経由で実行しないのが根本的な解決です。大量の投稿のインポートや置換は WP-CLI でサーバー上から実行する、プラグインがバックグラウンド処理(WP-Cron や Action Scheduler など)に対応していればそちらを使う、といった方法を検討してください。WP-CLI は PHP の CLI 版で動くため、PHP-FPM や Nginx のタイムアウトはかかりません。
Nginxの外側にもタイムアウトがある
Nginx と PHP の値をそろえても、手前の CDN やロードバランサーが先に切ることがあります。Cloudflare 経由なら独自のエラー画面(524 など)が出ますし、クラウドのロードバランサーにもアイドルタイムアウトの設定があります。既定値はサービスやプランによって異なり変更されることもあるため、利用しているサービスの最新ドキュメントで確認してください。Nginx のログにエラーが一切ないのに利用者側でタイムアウトする場合は、外側を疑います。
クライアント側は短めにして接続を守る
上流側とは逆に、クライアント側のタイムアウトは短めにするのが一般的です。ヘッダーや本文を極端にゆっくり送り続けて接続を占有する低速攻撃への対策になるからです。
# http { } 内の例。環境に合わせて調整してください
client_header_timeout 15s;
client_body_timeout 30s;
send_timeout 30s;
keepalive_timeout 30s;
大きなファイルのアップロードで失敗する場合、原因は時間よりも client_max_body_size(既定 1m)の不足で 413 が返っていることが多いので、ステータスコードを見て切り分けてください。
変更の手順
- 設定ファイルをバックアップする
- 値を変更し、
sudo nginx -t(PHP-FPM を変えたらsudo php-fpm8.3 -tも)で構文を確認する sudo systemctl reload nginx(と PHP-FPM の再読み込み)で反映する- 問題の操作を再現し、エラーログとアクセスログの時間を確認する
本番で試す前に、可能ならステージング環境で同じ処理を流して所要時間を測っておくと、必要な値を推測ではなく実測で決められます。
まとめ
- タイムアウトはクライアント側と上流側に分かれ、多くの項目は「全体の時間」ではなく「通信の間隔」の上限
- 504 はエラーログの
upstream timed outの文言から、接続・読み取りのどこで切れたかを読む - 同じリクエストについて、PHP 側の上限(
request_terminate_timeout・max_execution_time)を Nginx のfastcgi_read_timeoutより短くする request_terminate_timeoutはプール単位なので、管理画面だけ延ばすなら専用のプールを作って振り分ける- 重い処理は WP-CLI やバックグラウンド処理に移し、CDN やロードバランサーのタイムアウトも確認する