結論

504 Gateway Time-outは、中継するサーバーが奥のサーバーの返事を待ちきれなかったという意味です。閲覧者側の原因は少なく、多くはサイト側の遅さかタイムアウト設定です。

この記事でわかること

  • nginx で起こした504の画面とerror.log原文6種
  • 500〜504の違い表と、Apacheで502になる実例
  • nginx・ALB・CloudFrontなど6製品の既定の待ち時間

2026年10月17日更新|nginx 1.24.0・Apache 2.4.58 で実行確認

504 は、ブラウザとアプリのあいだにいる「中継役(ゲートウェイ)」が、決めた時間内に奥から返事をもらえなかったときに返すエラーです。

flowchart TD
    B[ブラウザ] --> G[中継役のサーバー<br>nginx・ALB・CloudFront]
    G --> A[奥のアプリ・API・DB]
    A -->|時間内に返事| OK[200 などを表示]
    A -.->|待ち時間を超えた| T[中継役が 504 を返す]

立場ごとに、最初にやることは次のとおりです。

立場・状況 最初にやること 詳しくは
閲覧者:ページを開いたら出た 1〜数分おいて再読み込み 閲覧者向けの対処
閲覧者:決済・送信の直後に出た 再送信せず、完了メールや履歴を見る 閲覧者向けの対処
運営者:自分のサイトで出た error.log の upstream timed out を探す 原因の切り分け
開発者:重い処理だけ504になる その処理の location だけ待ち時間を延ばす proxy_read_timeoutで直す
開発者:延ばしても直らない アプリ側の遅さ・別の層の上限を調べる 延ばしても直らないとき
AWSで出た ALB・CloudFront・API Gateway の上限を見る 既定のタイムアウト値

確認環境:Ubuntu 24.04・nginx 1.24.0(Ubuntu)・Apache 2.4.58(Ubuntu)・Python 3.11.15・curl 8.5.0・Chromium 141。わざと遅く返す上流サーバーを Python で作り、nginx と Apache の後ろに置いて 504・502・503 を実際に起こし、画面・error.log・かかった秒数を載せています。nginx・Apache の設定名と既定値は nginx.org と httpd.apache.org の公式ドキュメント、AWS の値は AWS の公式ドキュメント、Cloudflare の値は Cloudflare の公式ドキュメントで確かめました(確認日 2026年10月17日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「504 Gateway Time-outの原因と直し方|502・503との違いと実例」

504 Gateway Time-outとは

504 Gateway Timeout は、HTTP のステータスコード(サーバーが返す3桁の結果番号)の1つです。 HTTP の仕様(RFC 9110 の 15.6.5)で決められています。MDN は次のように説明しています。

サーバーがゲートウェイまたはプロキシとして動いているときに、リクエストを完了するために必要な応答を、上流のサーバーから時間内に受け取れなかったことを示す。

ここで出てくる言葉の意味は次のとおりです。

言葉 意味 例
ゲートウェイ・プロキシ ブラウザの代わりに奥のサーバーへ取り次ぐ中継役 nginx・Apache・ALB・CloudFront・API Gateway
上流(アップストリーム) 中継役から見た奥のサーバー Java・PHP・Python のアプリ、Lambda、別のAPI
タイムアウト 決めた時間を過ぎたので待つのをやめること nginx の既定は60秒

504 を返しているのは中継役で、遅いのはその奥です。 中継役のサーバー自体は動いています。止まっているか遅いのは、その奥のアプリ・データベース・外部API のどれかです。

「Gateway Time-out」と「Gateway Timeout」の表記

検索でよく見る 「504 Gateway Time-out」(ハイフンあり)は、nginx が返すエラーページの文言です。仕様上の名前は「Gateway Timeout」です。nginx 1.24.0 で実際に504を起こしたときの応答は次のとおりでした。

HTTP/1.1 504 Gateway Time-out
Server: nginx/1.24.0 (Ubuntu)
Date: Fri, 16 Oct 2026 17:11:05 GMT
Content-Type: text/html
Content-Length: 176
Connection: keep-alive

<html>
<head><title>504 Gateway Time-out</title></head>
<body>
<center><h1>504 Gateway Time-out</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>

画面の下に nginx と出ていれば、504 を返したのは nginx です。サイトによっては独自のエラーページに差し替えているため、「504 エラー」「ゲートウェイ タイムアウト」「サーバーからの応答がありません」など、表示は変わります。

500・501・502・503・504の違い

5つとも「サーバー側のエラー(5xx)」で、閲覧者のせいではない点は同じです。違いはどこが、何を見て諦めたかです。500・501 はアプリ(奥のサーバー)自身が返し、502・503・504 は主に中継役が返します。

コード 名前 何が起きたか よくある原因
500 Internal Server Error アプリが想定外のエラーで処理を続けられなかった 例外・バグ、設定ミス、DB のエラーを握りつぶさず返した
501 Not Implemented サーバーがそのメソッドを知らない・持っていない FOO など未知のメソッド、サーバーが実装していない機能
502 Bad Gateway 中継役に奥から不正な応答が来た、または接続を断られた アプリが落ちている、ポート違い、途中で接続が切れた
503 Service Unavailable サーバーが今は受け付けられない メンテナンス中、過負荷、受け付け先が1台も無い
504 Gateway Timeout 中継役に奥から時間内に返事が来なかった 処理が遅い、DB待ち、外部APIが遅い、通信が遮断

MDN は 503 について、一時的な状態に使い、可能なら Retry-After ヘッダーで復旧の目安を返すとしています。

500と501はいつ返すか(開発者向け)

500 は「ほかに合う 5xx が無いときの、最後の受け皿」です。 MDN は 500 を「予期しない状態で要求を果たせなかった」ことを示す、サーバーの問題全般の受け皿(catch-all)と説明しています。メンテナンス中なら 503、中継先の待ち時間切れなら 504 のように、もっと合うコードがあればそちらを返し、どれにも当てはまらない想定外のエラーに 500 を使います。

501 は「そのメソッドを、どのURLに対しても受け付けられない」ときだけです。 MDN の説明は次のとおりです。

  • サーバーがリクエストのメソッドを知らず、どのリソースにも対応できないときに返す。
  • GET と HEAD はサーバーが必ず対応するメソッドなので、これらに 501 を返してはいけない。
  • メソッドは知っているが、そのURLでは意図して許していないなら 405 Method Not Allowed を返す。
  • 501 は、キャッシュの指定が無ければ既定でキャッシュされうる。
状況 返すコード 例
処理中に想定外の例外が起きた 500 0 で割った、NULL を参照した
DB が止まっていて今は処理できない 503(復旧の目安があれば Retry-After) メンテナンス・接続数の上限
FOO など、サーバーが知らないメソッドが来た 501 打ち間違い・独自メソッド
DELETE は知っているが、このURLでは許していない 405(Allow ヘッダーで使えるメソッドを示す) 読み取り専用の一覧に DELETE
入力が間違っている 400 番台(400・404・422 など) 必須項目が空、存在しない ID

入力の誤りに 500 を返さないことが、いちばん多い直しどころです。500 は「サーバー側の不具合」という意味なので、閲覧者や呼び出し側は直しようがなく、監視の 5xx も増えます。

手元で、Python の http.server で作った小さなアプリ(ポート 25191)と、その前に置いた nginx 1.24.0(ポート 25192。/static/ は nginx が直接ファイルを返す)に、curl でリクエストを送った結果です(Python 3.11.15)。

送ったもの アプリに直接 nginx 経由
GET /orders/1(中で例外を受け止めて 500 を返す) 500 500(そのまま通る)
GET /crash(例外を受け止めずに落ちる) 応答なし(curl は 000) 502
FOO /orders/1(未知のメソッド) 501 501(アプリの応答が通る)
PATCH /orders/1・DELETE /orders/1 501 501
FOO・POST・DELETE /static/a.txt(nginx が直接返す) ― 405

読み取れることは次の3つです。

  • アプリが返した 500・501 は、nginx を通しても変わりません。 中継役は奥のコードをそのまま返します。
  • アプリが応答を返さずに落ちると、500 ではなく 502 になります。 nginx の error.log には upstream prematurely closed connection while reading response header from upstream が出ていました。500 になるのは、アプリ(またはフレームワーク)がエラーを受け止めて 500 を返したときだけです。
  • http.server は、処理を書いていないメソッドをすべて 501 にします。 応答は HTTP/1.0 501 Unsupported method ('FOO') でした。PATCH・DELETE のような一般的なメソッドにも 501 を返すため、MDN の使い分けでいえば、URL ごとに許していないだけなら 405 のほうが合います。nginx が直接返すファイルでは、POST・DELETE・FOO のどれも 405 でした。

nginx と Apache で実際に返ったコード

同じ「奥が遅い・落ちている」状況でも、中継役のソフトによって返るコードが違います。 同じ上流サーバーに対して、nginx 1.24.0 と Apache 2.4.58 で同じ状況を作った結果です。

起こした状況 nginx 1.24.0 Apache 2.4.58(mod_proxy_http)
奥が返事をしない(読み取りの待ち時間切れ) 504(5.006秒) 502 Proxy Error(5.006秒)
奥に接続できない(SYNを捨てる) 504(3.004秒) 503(3.003秒)
奥のポートが閉じている(接続拒否) 502(0.000秒) 503(0.001秒)
奥が返事をせずに接続を切った 502(3.001秒) ―

Apache の後ろで処理が遅いときは、504 ではなく 502 が出ることがあります。 このときの Apache の error.log には AH01102: error reading status line from remote server と (70007)The timeout specified has expired が出ていました。コードだけで判断せず、error.log の文言と「何秒で返ったか」を合わせて見ると原因に近づけます。

関連記事「HTTPステータスコード 一覧|全コードの原因と対処・curl実例つき」で扱っています。

503とRetry-After:いつ返し、どう待つか

503 は「今は一時的に受け付けられない。少し待てば戻る」という意味で、Retry-After ヘッダーで「いつ戻るか」を伝えられます。 RFC 9110 の 15.6.4 は、503 を「一時的な過負荷か計画メンテナンスのため、今はリクエストを処理できない状態」とし、待つ時間の目安として Retry-After を送ってよい(MAY)としています。

500・504との違い

コード 誰が・どんなときに返すか 待てば直るか
500 アプリが想定外のエラーに当たった 分からない(不具合なら直るまで続く)
503 サーバーがわざと「今は受け付けない」と返す 直る前提。Retry-After で目安を伝えられる
504 中継役が、奥の返事を待ちきれなかった 分からない(奥が遅い原因しだい)

503 は、サーバー側が状況を分かったうえで返すコードです。 500・504 は「何かが起きた」結果として出るのに対し、503 はメンテナンスや過負荷を自分で判断して返します。そのため、復旧の目安を Retry-After で添えられます。

Retry-Afterの2つの書き方(RFC 9110)

RFC 9110 の 10.2.3 では、値は HTTP-date(日時)か、秒数(0以上の整数)のどちらかです。RFC に載っている例は次の2行で、2行目は「2分後」を意味します。

Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
Retry-After: 120
書き方 合う場面 注意点
秒数(120) 過負荷・再起動など、終わる時刻が読めないとき 応答を受け取った時点から数える
HTTP-date(Sat, 17 Oct 2026 21:00:00 GMT) 終了時刻が決まった計画メンテナンス GMT で書く。サーバーの時計がずれると待ち時間もずれる

RFC 9110 は、3xx(リダイレクト)に付けた場合は「リダイレクト先へ行くまでの最短の待ち時間」の意味になるとしています。

503を返す場面と、429との違い

状況 返すコード Retry-After
計画メンテナンス(DB の移行・リリース作業) 503 終了時刻(HTTP-date)か秒数
過負荷で、一時的に新しい処理を断る 503 秒数(短め)
特定の利用者が短時間に送りすぎた(流量制限) 429 Too Many Requests その利用者が次に送れるまでの秒数
想定外の例外 500 付けない

429 は「あなたが送りすぎ」、503 は「サーバー全体が今は無理」です。 429 は RFC 6585 の 4 で決められた 4xx(呼び出し側の問題)のコードで、MDN は 429 にも Retry-After を付けて待つ時間を示せるとしています。

nginx 1.24 でメンテナンス中の503を返す

nginx 1.24.0 で、return 503 と add_header Retry-After 120 を書いて curl で呼びました。always を付けないと、503 の応答に Retry-After が付きませんでした。

# always なし
location / {
    add_header Retry-After 120;
    return 503;
}

# always あり
location / {
    add_header Retry-After 120 always;
    return 503;
}

always なしの応答です。Retry-After の行がありません。

HTTP/1.1 503 Service Temporarily Unavailable
Server: nginx/1.24.0 (Ubuntu)
Date: Sat, 17 Oct 2026 02:53:31 GMT
Content-Type: text/html
Content-Length: 206
Connection: keep-alive

always ありの応答です。最後に Retry-After: 120 が付きました。nginx の 503 の文言は「Service Temporarily Unavailable」です。

HTTP/1.1 503 Service Temporarily Unavailable
Server: nginx/1.24.0 (Ubuntu)
Date: Sat, 17 Oct 2026 02:53:31 GMT
Content-Type: text/html
Content-Length: 206
Connection: keep-alive
Retry-After: 120

<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>

nginx のドキュメントでは、add_header が付くのは 200・201・204・206・301・302・303・304・307・308 の応答だけで、always(1.7.5 から)を付けると応答コードに関係なく付きます。

503 に Retry-After を付けたい

付かない書き方add_header Retry-After 120;
→
付く書き方add_header Retry-After 120 always;

nginx 1.24.0 の curl -i で確認。エラー用ページに付けるヘッダーも同じく always が要る

メンテナンス用ページを出し、特定のIPだけ通す

本番でメンテナンスに入るときの設定です。error_page 503 で独自のページを 503 のまま返し、作業者の IP だけはふだんどおり通します。

geo $maintenance {
    default   1;
    127.0.0.2 0;          # 作業者の IP(本番では社内の固定 IP など)
}

server {
    listen 80;
    root /var/www/html;
    error_page 503 /maintenance.html;

    location = /maintenance.html {
        internal;                          # 直接開かせない
        add_header Retry-After 120 always;
        add_header Cache-Control "no-store" always;
    }

    location / {
        if ($maintenance) {
            return 503;
        }
        try_files $uri $uri/ =404;
    }
}

ふつうの IP(127.0.0.1)から呼ぶと、ステータスは 503 のまま、本文はメンテナンス用ページになりました。

HTTP/1.1 503 Service Temporarily Unavailable
Server: nginx/1.24.0 (Ubuntu)
Date: Sat, 17 Oct 2026 02:53:36 GMT
Content-Type: text/html
Content-Length: 220
Connection: keep-alive
ETag: "6ad2e324-dc"
Retry-After: 120
Cache-Control: no-store

<!doctype html>
<html lang="ja"><head><meta charset="utf-8"><title>メンテナンス中</title></head>
<body><h1>ただいまメンテナンス中です</h1><p>10月18日 6:00 に再開予定です。</p></body></html>

作業者の IP(curl --interface 127.0.0.2)からは、ふだんのページが 200 で返りました。

HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Sat, 17 Oct 2026 02:53:36 GMT
Content-Type: text/html
Content-Length: 7
…
app ok
確かめたこと 結果
ふつうの IP で / 503・メンテナンス用ページ・Retry-After: 120
作業者の IP で / 200・ふだんのページ
/maintenance.html を直接開く 404(internal のため)

メンテナンス用ページを 200 で返さないことが要点です。200 で返すと、メンテナンスの文言がそのURLの中身として扱われることがあります。Cache-Control: no-store は、CDN やブラウザがメンテナンス用ページを覚えて、終わった後も出し続けることを防ぎます。

受け取る側はRetry-Afterをどう扱うか

Retry-After を読んで待つかどうかは、受け取る側しだいです。 実際に試した結果と、ドキュメントの記述をまとめます。

受け取る側 Retry-After の扱い 根拠
ブラウザ 503 の画面を表示するだけ。自動で再読み込みしない Chromium 141 で12秒待ってもリクエストは1回
curl --retry 書かれた秒数・日時だけ待って再試行する curl 8.5.0 で計測(下の表)
Googlebot 503・429 の Retry-After を再クロールの目安にする Google 検索セントラル
自作のクライアント 自分で読んで待つ処理を書く ―

ブラウザの結果は、Retry-After: 3 を付けた 503 のページを Chromium 141(ヘッドレス)で開き、12秒待ったときのものです。nginx のアクセスログに残ったブラウザからのリクエストは1回だけで、画面は 503 のページのままでした。

curl のマニュアル(curl --manual)には、--retry は一時的なエラー(タイムアウト、HTTP の 408・429・500・502・503・504)で再試行し、Retry-After: があればそれに従って次の再試行の時刻を決める(7.66.0 から)とあります。curl 8.5.0 で、待った秒数を測りました。

curl -o out.html -w 'status=%{http_code}\n' --retry 2 http://127.0.0.1:25153/
Warning: Problem : HTTP error. Will retry in 3 seconds. 2 retries left.
Warning: Problem : HTTP error. Will retry in 3 seconds. 1 retries left.
status=503
返したヘッダー curl のオプション かかった時間
Retry-After: 3 --retry 2 6.01秒(3秒×2回)
なし --retry 2 3.14秒(1秒→2秒と倍に増える)
なし --retry 3 7.02秒(1秒→2秒→4秒)
Retry-After: 3 --retry 2 --retry-delay 1 6.06秒(Retry-After が優先)
8秒後の日時(HTTP-date) --retry 1 8.04秒(「Will retry in 8 seconds」)
Retry-After: 120 --retry 1 --retry-max-time 10 0.03秒(再試行せずに終了)

最後の行では、待つと上限を超えるため、curl は再試行せずに次の警告を出して終わりました。

Warning: The Retry-After: time would make this command line exceed the maximum 
Warning: allowed time for retries.

-o /dev/null と --retry を一緒に使うと、curl 8.5.0 では再試行の前に curl: Failed to truncate file で止まりました(終了コード 23)。 再試行のときに出力先のファイルを空にし直すためです。-o out.html のように、ふつうのファイルに書き出すと最後まで再試行しました。

503を長く続けたときの検索への影響

Google 検索セントラルの「Google のクロール頻度を下げる」のページ(2026年10月6日更新)には、次のことが書かれています。

  • 短い時間(数時間、または1〜2日)なら、200 の代わりに 500・503・429 を返してクロールを減らせる。エラーが減れば、クロールの頻度は自動で戻る。
  • 503・429 を返すときは、Retry-After で Google のクローラーがいつ再試行できるかを、秒数か UTC の日時で示せる。
  • 1〜2日より長く続けることは勧めない。同じ URL で何日も続くと、インデックスから外れることがある。

長いメンテナンスでは、Retry-After に終わる時刻を書き、作業を1〜2日以内に収めるのが目安です。それより長く止めるなら、サイト全体を 503 にせず、止める機能のURLだけを 503 にする分け方もあります。

アプリ(Spring Boot)の中で、業務の例外を 503 と Retry-After に変えて返す書き方は、関連記事「Spring Boot 例外処理|ステータスコードの返し分けと503実例」で扱っています。

504エラーが出たときの対処(閲覧者向け)

504 の原因はほとんどがサイト側にあり、閲覧者ができることは限られます。それでも、次の順に試すと切り分けられます。

1

1〜数分おいて、ページを再読み込みする

アクセスが集中した一時的な遅れなら、時間をおくと開けます。何度も連打すると、サイトの負荷を上げることになります。

Windows は F5、Mac は ⌘+R

2

決済・申し込み・投稿の直後なら、再送信しない

504 が出ても、サイトの奥では処理が最後まで終わっていることがあります(下の「延ばしても直らないとき」で実際に確かめています)。完了メール・注文履歴・投稿一覧で結果を見てから、やり直すか決めます。

二重の注文・二重の支払いを防ぐ

3

ほかのサイトが開けるかを見る

ほかのサイトも開けないなら、自分の回線・Wi-Fi・社内ネットワークの問題です。そのサイトだけなら、サイト側の問題の可能性が高くなります。

4

VPN・プロキシを切る、別の回線で開く

MDN は、ほかの人は使えているのに自分だけ504になる場合、VPN や独自のネットワーク設定、ファイアウォール・プロキシ・DNS の設定が原因のこともあるとしています。スマホのモバイル回線で開けるかを試すと切り分けられます。

5

続くなら、サイトの運営者に伝える

開いたURL・時刻・表示された文言(「504 Gateway Time-out」と下の「nginx」など)を伝えると、運営者がログから探しやすくなります。

504の原因の切り分け(運営者・開発者向け)

運営者・開発者が最初に見るのは、中継役のエラーログと504 が返るまでの秒数です。

まず見る2つ

中継役の error.log(nginx なら upstream timed out の行)

504 が返るまでの秒数(curl の time_total やアクセスログ)

そこから分かること

接続の段階か、返事待ちの段階か、途中の送信中か

どの層(nginx・ALB・CloudFront・API Gateway)の上限に当たったか

504 が返るまでの秒数で、どの層かを当てる

504 が返るまでの秒数は、どこかの待ち時間の上限とほぼ一致します。 既定値のままなら、秒数からどの層が諦めたかの見当がつきます(値は後の「既定のタイムアウト値」の表のとおり、公式ドキュメントで確かめたもの)。

504までの秒数 当たっている可能性がある上限 確かめる設定
約60秒 nginx・Apache・ALB の既定 proxy_read_timeout・Timeout・アイドルタイムアウト
約30秒 CloudFront の応答タイムアウト、API Gateway の HTTP API オリジンの Response timeout、統合タイムアウト
約29秒 API Gateway の REST API 統合タイムアウト
約10秒 ALB のターゲットへの接続 セキュリティグループ・ネットワークACL
上限の2倍・3倍 中継役が別のサーバーで再試行した nginx の proxy_next_upstream
すぐ返る 奥のサーバー自身が504を返している 奥のサーバーのログ

curl で秒数とコードを同時に見るコマンドです。

curl -s -o /dev/null -w 'status=%{http_code} time=%{time_total}s\n' 'https://example.com/slow-page'

nginx 1.24.0 の既定のまま、65秒かかる処理を呼んだときの出力です。60.06秒で504 になり、既定の proxy_read_timeout 60s と一致しました。

status=504 time=60.061462s

nginx のアクセスログに上流の時間を出す

nginx の標準のアクセスログには上流の時間が出ません。log_format に $upstream_response_time(上流の応答にかかった秒数)などを足すと、どの上流で何秒かかったかが1行で分かります。

http {
    log_format timing '$remote_addr [$time_local] "$request" $status '
                      'rt=$request_time uct=$upstream_connect_time '
                      'urt=$upstream_response_time us=$upstream_status ua=$upstream_addr';
    access_log /var/log/nginx/timing.log timing;
}

実際に出た行です。3行目は接続を断られた502、4行目は上流2台に順に問い合わせて、どちらも5秒で諦めた504です。

127.0.0.1 [17/Oct/2026:02:14:52 +0900] "GET /short/slow?sec=1 HTTP/1.1" 200 rt=1.001 uct=0.001 urt=1.001 us=200 ua=127.0.0.1:25101
127.0.0.1 [17/Oct/2026:02:14:57 +0900] "GET /short/slow?sec=8 HTTP/1.1" 504 rt=5.005 uct=0.000 urt=5.005 us=504 ua=127.0.0.1:25101
127.0.0.1 [17/Oct/2026:02:14:57 +0900] "GET /down/ HTTP/1.1" 502 rt=0.000 uct=- urt=0.000 us=502 ua=127.0.0.1:25199
127.0.0.1 [17/Oct/2026:02:15:18 +0900] "GET /two/slow?sec=8 HTTP/1.1" 504 rt=10.010 uct=0.000, 0.000 urt=5.005, 5.006 us=504, 504 ua=127.0.0.1:25101, 127.0.0.1:25102

すでにあるアクセスログから、502・503・504 の件数を数えるときは次のようにします(標準の combined 形式では9列目がステータス)。

awk '$9 ~ /^50[234]$/ {print $9}' /var/log/nginx/access.log | sort | uniq -c
      3 502
      5 504

nginxで504を再現する

原因を確かめるには、わざと504を起こして、ログの文言を自分の目で見ておくのが近道です。ここでは、指定した秒数だけ待ってから返す上流サーバーを Python で作り、nginx の後ろに置きました。

わざと遅く返す上流サーバー(Python)

# upstream.py  使い方: python3 upstream.py 25101
import socket, sys, time
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs

class H(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def do_GET(self):
        u = urlparse(self.path)
        q = {k: v[0] for k, v in parse_qs(u.query).items()}
        if u.path == "/slow":    # 何も返さずに sec 秒待ってから返す
            sec = float(q.get("sec", "0"))
            time.sleep(sec)
            body = f"done after {sec:g}s\n".encode()
            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
        elif u.path == "/drip":  # ヘッダーを先に返し、every 秒ごとに1行ずつ送る
            n, every = int(q.get("n", "5")), float(q.get("every", "3"))
            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.send_header("Transfer-Encoding", "chunked")
            self.end_headers()
            for i in range(n):
                time.sleep(every)
                line = f"line {i+1}\n".encode()
                self.wfile.write(b"%x\r\n%s\r\n" % (len(line), line))
                self.wfile.flush()
            self.wfile.write(b"0\r\n\r\n")
        elif u.path == "/die":   # sec 秒後に、何も返さず接続を切る
            time.sleep(float(q.get("sec", "3")))
            self.close_connection = True
            self.connection.shutdown(socket.SHUT_RDWR)
        else:
            self.send_response(200)
            self.send_header("Content-Length", "3")
            self.end_headers()
            self.wfile.write(b"ok\n")

    do_POST = do_GET

ThreadingHTTPServer(("127.0.0.1", int(sys.argv[1])), H).serve_forever()

nginx の設定

upstream app { server 127.0.0.1:25101; }

server {
    listen 127.0.0.1:25100;

    # 既定のまま(proxy_read_timeout 60s)
    location / {
        proxy_pass http://app;
    }

    # 試験用に読み取りの待ち時間を5秒にした場所
    location /short/ {
        proxy_pass http://app/;
        proxy_read_timeout 5s;
    }
}

結果:5秒で504、3秒なら200

curl -s -o /dev/null -w 'status=%{http_code} time=%{time_total}s\n' 'http://127.0.0.1:25100/short/slow?sec=8'
curl -s -w 'status=%{http_code} time=%{time_total}s\n' 'http://127.0.0.1:25100/short/slow?sec=3'
status=504 time=5.006084s
done after 3s
status=200 time=3.001488s

このとき nginx の error.log に出た行です。

2026/10/17 02:11:21 [error] 8535#8535: *3 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: , request: "GET /short/slow?sec=8 HTTP/1.1", upstream: "http://127.0.0.1:25101/slow?sec=8", host: "127.0.0.1:25100"

while reading response header from upstream は、「奥にはつながったが、返事の最初の部分(ヘッダー)が待ち時間内に来なかった」という意味です。 504 でいちばん多い形で、upstream: のところに、どの上流の何のURLで待たされたかが出ます。

error.logのエラー原文と原因の一覧

nginx 1.24.0 で状況を作って出た error.log の原文です。while の後ろを見ると、どの段階で止まったかが分かります。

error.log の原文(要部) 返ったコード 止まった段階 主な直し方
upstream timed out (110: Connection timed out) while reading response header from upstream 504 返事の最初が来ない 処理を速くする、その場所だけ proxy_read_timeout を延ばす
upstream timed out (110: Connection timed out) while connecting to upstream 504 接続できない ファイアウォール・セキュリティグループ・宛先IP を確かめる
upstream timed out (110: Connection timed out) while reading upstream 200(途中で切れる) 返事の途中で止まった 少しずつ返す処理の間隔を短くする、待ち時間を延ばす
connect() failed (111: Connection refused) while connecting to upstream 502 接続を断られた アプリの起動・ポート番号を確かめる
upstream prematurely closed connection while reading response header from upstream 502 返事の前に切られた アプリ側のタイムアウト(ワーカーの制限時間など)を確かめる
no live upstreams while connecting to upstream 502 上流が全部「一時停止」扱い 直前の504の原因を直す(下の節を参照)

3行目の while reading upstream は見落としやすい形です。ヘッダーがすでに返ったあとで止まったため、ステータスは200のまま、本文が途中で切れます。 curl では次のように出ました。

curl: (18) transfer closed with outstanding read data remaining
status=200 time=5.005427s

接続の段階の504(2行目)は、宛先が SYN(接続の申し込み)を捨てる状態を iptables で作り、proxy_connect_timeout 3s で起こしました。3.004秒で504が返りました。

関連記事「Nginxリバースプロキシ設定|proxy_passの書き方と502/504対処」で扱っています。

proxy_read_timeoutで504を直す

処理が遅いことが分かっていて、すぐには速くできないときは、その処理のURLだけ待ち時間を延ばします。サーバー全体(http や server)で延ばすと、本当に固まった処理でも長く待ち続け、接続が詰まりやすくなります。

nginx のタイムアウトの設定名と既定値

nginx.org のドキュメントで確かめた値です。どれも http・server・location に書けます。

設定名 既定値 何の待ち時間か
proxy_read_timeout 60s 上流からの2回の読み取りのあいだの待ち時間
proxy_connect_timeout 60s 上流への接続を確立するまで(通常75秒を超えられない)
proxy_send_timeout 60s 上流へ2回の書き込みのあいだの待ち時間
fastcgi_read_timeout 60s PHP-FPM など FastCGI からの読み取りのあいだ

重い処理の location だけ延ばす

# 帳票の作成だけ120秒待つ
location /report/ {
    proxy_pass http://app/;
    proxy_read_timeout 120s;
}

設定を変えたら、文法を確かめてから読み込み直します。

sudo nginx -t && sudo systemctl reload nginx

既定のままでは60.06秒で504になった65秒の処理が、この location では最後まで返りました。

done after 65s
status=200 time=65.002562s

「全体の時間」ではなく「読み取りのあいだ」の時間

ドキュメントには、proxy_read_timeout は応答全体の時間ではなく、2回の読み取りのあいだにだけかかると書かれています。待ち時間5秒のまま、4秒ごとに1行ずつ返す処理を呼ぶと、合計20秒かかっても504になりませんでした。

line 1
line 2
line 3
line 4
line 5
status=200 time=20.002488s

長い処理の途中経過を少しずつ返す作り(ストリーミング)なら、待ち時間を延ばさなくても切れません。一方で、間隔が7秒になると5秒で切れ、前の節の「200のまま途中で切れる」形になりました。

関連記事「タイムアウト設計の値の決め方|接続・読み取りの違いと多段の計算例」で扱っています。

504がタイムアウトを延ばしても直らないとき

待ち時間を延ばす前と後に、次の点を確かめます。実際に動かして分かった、見落としやすい点です。

60.06秒nginx 既定で504になるまで65秒かかる処理を既定の設定で呼んだとき
+5秒504のあとも処理は続いて完了上流のログでは、504の5秒後に200で終わっていた
10.01秒上流2台で待ち時間が2倍にGET は次のサーバーで再試行される

504 が出ても、奥の処理は止まっていない

nginx が 02:11:05 に504を返した65秒の処理は、上流のログでは 02:11:10 に 200 で終わっていました。 POST でも同じで、504 の3秒後に上流の処理が完了していました。

[upstream] 02:11:10 "GET /slow?sec=65 HTTP/1.0" 200 -
[upstream] 02:13:52 "POST /slow?sec=8 HTTP/1.0" 200 -

中継役が待つのをやめても、奥のアプリは処理を続けます。 注文・送金・メール送信のような処理で504が出たあと、利用者が送り直すと二重に実行されます。同じリクエストを2回受けても1回分しか実行しない作り(冪等にする)か、時間のかかる処理を受け付けだけ先に返して後で処理する作り(非同期化)にすると防げます。

上流が複数台だと、待ち時間が台数分に伸びる

nginx の proxy_next_upstream の既定は error timeout で、タイムアウトしたら次のサーバーに同じリクエストを送ります。上流2台・待ち時間5秒で試すと、GET は2台に順に送られ、10.01秒で504 になりました。

ドキュメントには、POST・LOCK・PATCH はすでに上流に送ったあとなら次のサーバーへ送らない(1.9.13 から)とあります。実際に POST は1台だけに送られ、5.00秒で504になりました。

504 の直後に 502 no live upstreams が出る

上の GET で2台ともタイムアウトした直後、別の POST を送ると、待たずに 502 が返り、error.log には次の行が出ました。

[warn] ... upstream server temporarily disabled while reading response header from upstream, ... upstream: "http://127.0.0.1:25101/slow?sec=8"
[error] ... no live upstreams while connecting to upstream, ... request: "POST /two/slow?sec=8 HTTP/1.1", upstream: "http://app2/slow?sec=8"

タイムアウトした上流は、しばらく「使えない」扱いになります。11秒おいて送り直すと、また上流に送られて504(5.00秒)になりました。502 と 504 が交互に出ているときは、根本は504の原因(遅さ)です。

奥のアプリにも、別の待ち時間がある

中継役だけ延ばしても、その奥にも上限があります。アプリのサーバーが処理の途中で接続を切ると、nginx 側は504ではなく 502(upstream prematurely closed connection) になります(上の一覧の5行目)。

層 待ち時間の例 超えたときの見え方
CDN CloudFront の応答タイムアウト 30秒 CloudFront が504
ロードバランサー ALB のアイドルタイムアウト 60秒 ALB が504
Webサーバー nginx の proxy_read_timeout 60秒 nginx が504
アプリのサーバー ワーカーの制限時間、PHP の実行時間の上限など nginx が502
DB・外部API クエリの上限、HTTPクライアントのタイムアウト アプリが500などを返す

外側の層ほど待ち時間を長く、内側ほど短くすると、内側のエラーが外側まで届きます。 逆だと、内側は処理を続けているのに外側が先に504を返し、原因がログに残りにくくなります。

関連記事「Pythonのrequestsの使い方|タイムアウト・エラー処理・再試行まで」で扱っています。

Apache・AWS ALB・CloudFront・API Gatewayの既定のタイムアウト値

2026年10月17日時点の公式ドキュメントで確かめた値です。

製品 既定の待ち時間 設定名・上限 超えたときのコード
nginx 読み取り60秒・接続60秒 proxy_read_timeout・proxy_connect_timeout 504
Apache httpd 2.4 60秒 Timeout。ProxyTimeout が無ければ Timeout を使う 502・503(実測)
AWS ALB アイドル60秒・接続10秒 idle_timeout.timeout_seconds(1〜4000秒) 504
Amazon CloudFront 応答30秒・接続10秒×3回 Response timeout(1〜120秒、引き上げ申請可) 504
API Gateway(REST) 最大29秒 統合タイムアウト(50ミリ秒〜29秒、リージョン・プライベートは申請で延長可) 504
API Gateway(HTTP API) 最大30秒 統合タイムアウト(延長不可) ―

表を補う点です。

  • Apache:Ubuntu 24.04 の Apache パッケージに入っている /etc/apache2/apache2.conf は Timeout 300 になっていました。ドキュメントの既定(60秒)と、ディストリビューションの設定ファイルの値が違うことがあります。
  • ALB:AWS のドキュメントは、504 の原因として、接続タイムアウト(10秒)までに接続できない、アイドルタイムアウトまでにターゲットが応答しない、サブネットのネットワークACLが一時ポート(1024〜65535)を通さない、Content-Length が本文より大きい、などを挙げています。
  • CloudFront:オリジン(元のサーバー)のファイアウォールやセキュリティグループが CloudFront を通さない場合も504になる、とドキュメントにあります。既定の接続10秒×3回で、別のオリジンへ切り替えるかエラーを返すまでに最大30秒かかります。
  • API Gateway(REST):延長はできますが、ドキュメントには、リージョン単位のスロットリング(流量制限)の上限が下がる場合があると書かれています。統合のタイムアウトは INTEGRATION_TIMEOUT(既定のステータス504)のゲートウェイレスポンスになります。
  • Cloudflare:上流が時間内に応答しないときは 504 ではなく 524 を返します。ドキュメントでは Proxy Read Timeout の既定は125秒です。

関連記事「ALBをAWS CLIで作成|ターゲットグループ・リスナー・ルール設定」で扱っています。

504エラーはSEOに影響するか

Google 検索セントラルのドキュメントでは、5xx のエラーが返ると Google のクローラーは一時的にクロールを遅くし、2xx に戻ると徐々に元に戻すとしています。長く続くと、すでにインデックスされたURLも、最終的にはインデックスから外れると書かれています。数分の504で順位が落ちることを心配するより、504が続く状態を早く見つけて直す仕組み(監視と、上の切り分け)を持つほうが効果があります。

よくある質問

Q. 504 Gateway Time-outとは何ですか? A. 中継役のサーバー(nginx・ロードバランサー・CDN など)が、奥のサーバーの返事を決めた時間内にもらえなかったことを示すエラーです。詳しくは「504 Gateway Time-outとは」の節にあります。

Q. 504 エラーは自分のパソコンやスマホのせいですか? A. 多くはサイト側の問題です。ほかのサイトも開けないとき、VPN やプロキシを使っているときは、自分の側の可能性があります。「閲覧者向けの対処」の手順で切り分けられます。

Q. http 504 と 502 の違いは何ですか? A. 504 は「返事が時間内に来なかった」、502 は「不正な応答が来た・接続を断られた」です。ただし Apache の mod_proxy_http では、読み取りの待ち時間切れでも502が返りました。「500・501・502・503・504の違い」に表があります。

Q. 500 Internal Server Error と 504 の違いは何ですか? A. 500 はアプリ自身が「想定外のエラーで処理できなかった」と返すコード、504 は中継役が「奥から時間内に返事が来なかった」と返すコードです。アプリが返した 500 は nginx を通してもそのまま 500 で届きます。「500と501はいつ返すか」に実例があります。

Q. 501 Not Implemented はどんなときに使いますか? A. サーバーがリクエストのメソッドを知らず、どの URL でも対応できないときです。メソッドは知っているがその URL では許していないなら 405 を返します。GET と HEAD に 501 は返しません。

Q. 503 Service Unavailable と Retry-After はどう使いますか? A. 503 はメンテナンスや過負荷で「今は一時的に受け付けない」ときに返し、Retry-After に再開までの秒数か日時を書きます。nginx では add_header Retry-After 120 always; のように always を付けないと 503 に付きません。ブラウザは自動で再読み込みせず、curl の --retry は Retry-After の秒数だけ待ちました。「503とRetry-After:いつ返し、どう待つか」に実例があります。

Q. gateway timeout はどれくらい待つと出ますか? A. 設定によります。nginx・Apache・ALB の既定は60秒、CloudFront は30秒、API Gateway の REST API は最大29秒です。504が返るまでの秒数から、どの層かの見当がつきます。

Q. 504エラーの原因は何ですか? A. 奥の処理が遅い(重いSQL・外部APIの待ち・負荷)、接続が遮断されている(ファイアウォール・セキュリティグループ)、待ち時間の設定が短い、のどれかが多いです。nginx なら error.log の upstream timed out ... while の後ろで段階が分かります。

Q. 504 gateway timeout を nginx で直すには? A. 遅い処理の location だけ proxy_read_timeout を延ばし、nginx -t のあと reload します。延ばす前に、処理そのものを速くできないか、奥のアプリの待ち時間が先に切れないかも確かめます。

まとめ

  • 504 は、中継役が奥の返事を待ちきれなかったときのエラー。返しているのは中継役で、遅いのはその奥。
  • 閲覧者は、時間をおいて再読み込み。決済・送信の直後なら再送信せず、結果を先に確かめる。
  • 運営者は、error.log の upstream timed out ... while ... と、504までの秒数で段階と層を特定する。
  • nginx は遅い処理の location だけ proxy_read_timeout を延ばす。待ち時間は「読み取りのあいだ」の時間。
  • 504 のあとも奥の処理は続く。二重実行を防ぐ作り(冪等・非同期化)が根本の対策になる。
  • Apache の mod_proxy_http では、読み取りの待ち時間切れで502、接続の待ち時間切れで503が返った。

参考・出典

確認日はいずれも 2026年10月17日です。

  • MDN「500 Internal Server Error」:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/500
  • MDN「501 Not Implemented」:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/501
  • MDN「504 Gateway Timeout」:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/504
  • MDN「502 Bad Gateway」:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/502
  • MDN「503 Service Unavailable」:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/503
  • MDN「Retry-After」:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After
  • MDN「429 Too Many Requests」:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429
  • RFC 9110 HTTP Semantics(10.2.3 Retry-After・15.6 Server Error 5xx):https://www.rfc-editor.org/rfc/rfc9110
  • RFC 6585 Additional HTTP Status Codes(4. 429 Too Many Requests):https://www.rfc-editor.org/rfc/rfc6585
  • nginx「Module ngx_http_headers_module」(add_header):https://nginx.org/en/docs/http/ngx_http_headers_module.html
  • curl「curl.1 the man page」(--retry):https://curl.se/docs/manpage.html
  • nginx「Module ngx_http_proxy_module」:https://nginx.org/en/docs/http/ngx_http_proxy_module.html
  • nginx「Module ngx_http_fastcgi_module」:https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html
  • Apache HTTP Server 2.4「core - Timeout」:https://httpd.apache.org/docs/2.4/mod/core.html#timeout
  • Apache HTTP Server 2.4「mod_proxy - ProxyTimeout」:https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#proxytimeout
  • AWS「Edit attributes for your Application Load Balancer」:https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-load-balancer-attributes.html
  • AWS「Troubleshoot your Application Load Balancers」:https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-troubleshooting.html
  • AWS「Origin settings(CloudFront)」:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistValuesOrigin.html
  • AWS「Quotas(CloudFront)」:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html
  • AWS「HTTP 504 status code (Gateway Timeout)(CloudFront)」:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/http-504-gateway-timeout.html
  • AWS「Amazon API Gateway quotas」:https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-execution-service-limits-table.html
  • AWS「Quotas for configuring and running an HTTP API」:https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-quotas.html
  • AWS「Gateway response types for API Gateway」:https://docs.aws.amazon.com/apigateway/latest/developerguide/supported-gateway-response-types.html
  • Cloudflare「Error 524」:https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/error-524/
  • Google 検索セントラル「HTTP status codes, network and DNS errors」:https://developers.google.com/crawling/docs/troubleshooting/http-status-codes
  • Google 検索セントラル「Google のクロール頻度を下げる」:https://developers.google.com/crawling/docs/crawlers-fetchers/reduce-crawl-rate

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「504 Gateway Time-outの原因と直し方|502・503との違いと実例」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?