エラーの内容
504 Gateway Timeout
Nginx がリバースプロキシとして動作しており、アップストリームサーバー(PHP-FPM、Node.js、Gunicorn など)が時間内に応答しませんでした。Nginx は待ち続け、タイムアウト上限に達して処理を諦めた状態です。
注意点として、これは 502 とは異なります。502 はアップストリームが不正なレスポンスを返したか、まったく到達できなかったことを意味します。504 は接続自体は確立されていたものの、アップストリームの処理が時間内に完了しなかったことを意味します。
発生原因
- 遅いデータベースクエリや外部 API 呼び出しがリクエストを長時間ブロックしている — ダッシュボードページで 10 秒かかる DB クエリは典型的な原因です
- アップストリームプロセス(PHP-FPM ワーカー、Node プロセスなど)が過負荷状態かループに陥っている
- Nginx のデフォルトの 60 秒タイムアウトが、ファイルアップロード・CSV エクスポート・レポート生成などの処理に対して短すぎる
- PHP の
max_execution_timeが Nginx のタイムアウトより先にスクリプトを強制終了し、アップストリームがリクエストの途中でドロップする
ステップ 1 — まず Nginx のエラーログを確認する
設定ファイルはまだ触らないでください。Nginx が実際に何を報告しているか確認します:
sudo tail -f /var/log/nginx/error.log
通常、次のようなメッセージが表示されます:
[error] upstream timed out (110: Connection timed out) while reading response header from upstream
この行は、Nginx がレスポンスヘッダーの待機中にタイムアウトしたことを示しています。アップストリームは処理を開始したものの、十分な速さで完了できなかったということです。これが問題の核心です。
ステップ 2 — Nginx のプロキシタイムアウトを延長する
Nginx のデフォルトの proxy_read_timeout は 60 秒です。PDF 生成、サードパーティ API 呼び出し、大きなファイルのアップロード処理など、実際の作業を伴う処理では 60 秒はあっという間に過ぎてしまいます。
サーバーブロックの設定ファイル(通常は /etc/nginx/sites-available/yoursite または /etc/nginx/conf.d/yoursite.conf)を開きます:
sudo nano /etc/nginx/sites-available/yoursite
location ブロック内に以下のディレクティブを追加します:
location / {
proxy_pass http://127.0.0.1:8000;
proxy_connect_timeout 60s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
}
- proxy_connect_timeout — アップストリームへの接続確立を待つ時間
- proxy_send_timeout — アップストリームへのリクエスト送信を待つ時間
- proxy_read_timeout — アップストリームからの応答を待つ時間(504 の直接原因となるディレクティブです)
120 秒でほとんどのケースに対応できます。バッチジョブや大容量ファイルのアップロードには 300 秒に設定してください。グローバルに 600 秒を設定するのは避けましょう — 詳細はヒントセクションで説明します。
設定をテストしてリロードします:
sudo nginx -t
sudo systemctl reload nginx
ステップ 3 — PHP-FPM のタイムアウトを修正する(PHP を使用している場合)
PHP には独自の実行時間制限があります。Nginx のタイムアウトが発動する前にスクリプトを強制終了することがあります。/etc/php/8.x/fpm/php.ini を確認します:
max_execution_time = 120
request_terminate_timeout = 120
PHP-FPM プール設定ファイル(/etc/php/8.x/fpm/pool.d/www.conf)も更新します:
request_terminate_timeout = 120
設定を反映するために再起動します:
sudo systemctl restart php8.2-fpm
ステップ 4 — アップストリームアプリのパフォーマンスを確認する
タイムアウトを延長するのは一時的な対処です。アップストリームがなぜ遅いのかを把握する必要があります。
アップストリームプロセスは正常に動作していますか?
# PHP-FPM の場合
sudo systemctl status php8.2-fpm
# Node.js / Gunicorn の場合
ps aux | grep node
ps aux | grep gunicorn
全ワーカーが飽和状態になっていませんか? PHP-FPM ワーカーが 5 つしかないサイトで 50 の同時リクエストを処理しようとすると、リクエストがキューに積まれてタイムアウトします。アクティブなワーカーを確認します:
# PHP-FPM ステータス(プール設定で有効化されている場合)
curl http://127.0.0.1/fpm-status
# プロセスの全体確認
top -bn1 | grep -E "(php|node|gunicorn|uwsgi)"
ワーカーが上限に達していますか?プールの上限を増やしてください。/etc/php/8.x/fpm/pool.d/www.conf を編集します:
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
ステップ 5 — 遅いクエリ?データベースを確認する
特定のページ(ダッシュボード、レポート、エクスポートエンドポイント)でのみ 504 が発生する場合、ほぼ確実に遅い DB クエリが原因です。MySQL のスロークエリログを有効にして問題のクエリを特定します:
# /etc/mysql/mysql.conf.d/mysqld.cnf に追記
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
sudo systemctl restart mysql
sudo tail -f /var/log/mysql/slow.log
5〜60 秒かかるクエリがありますか?それが 504 の原因です。200 万行のテーブルにインデックスがないと、2 ミリ秒のクエリが 45 秒かかることがあります。インデックスを追加し、クエリを書き直すか、結果をキャッシュしてください。
修正確認
変更を加えた後、以下のチェックリストを実行します:
# Nginx 設定の検証
sudo nginx -t
# リロード
sudo systemctl reload nginx
# エラーログをリアルタイムで監視
sudo tail -f /var/log/nginx/error.log
# 遅いエンドポイントに直接アクセスして時間を計測
curl -w "\nTime total: %{time_total}s\n" -o /dev/null -s https://yoursite.com/slow-endpoint
time_total がタイムアウト値を下回り、エラーログに何も出なくなれば、修正は完了です。
追加のヒント
- タイムアウトの適用範囲を限定する — グローバルに設定を大きくしないでください。長いタイムアウトは必要な特定の
locationブロック(/api/export、/uploadなど)にのみ適用してください。300 秒のグローバルタイムアウトは Slow Loris DoS 攻撃の温床になります。 - 長時間の処理はキューにオフロードする — 処理に数分かかる場合、タイムアウトと戦うのではなく、処理をバックグラウンドキュー(Redis + ワーカー)に回し、ジョブ ID をすぐに返して、クライアントが結果をポーリングする設計にしましょう。タイムアウトは発生しなくなります。
- FastCGI の場合は fastcgi_read_timeout を使用する — PHP に
proxy_passではなくfastcgi_passを使っている場合、proxy_read_timeoutではなく以下のディレクティブが必要です:
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_read_timeout 120s;
include fastcgi_params;
}

