tech

VPSのログに503が出ていた。レート制限は正しく動いていたが、返すコードが問題だった

2026-05-10完了#vps#nginx#security
VPSのログに503が出ていた。レート制限は正しく動いていたが、返すコードが問題だった

毎朝 VPS のアクセスログをまとめたレポートを確認しています。

ある日の集計を見ていると、気になる数字がありました。 その日のリクエスト件数は2,309件。前日は5,237件。 毎日これだけの数のアクセスが来ています。

多くは攻撃者の自動スキャンツールによるものです。 存在しないパスへのアクセスなどを示す4xxエラーが966件あり、 正規の閲覧者によるアクセスとは明らかに性質が違います。

そのなかに、5xxエラーが5件ありました。

5xxエラーは「サーバー側の問題」を意味する

HTTP のステータスコードは、3桁の数字で分類されています。

  • 2xx:成功(ページを正常に返せた)
  • 3xx:リダイレクト(別の URL に転送した)
  • 4xx:クライアント側のエラー(存在しないページへのアクセスなど)
  • 5xx:サーバー側のエラー

5xx が出ると「サーバーに何か問題が起きた」という意味になります。 ブログが正常に動いているつもりでも、5xx が記録されていたら見過ごせません。

5xxの内訳を確認した

5件の5xxのうち、1件は別の要因による一時的なものでした。

残り4件はすべて 503 Service Unavailable で、同じ種類のエンドポイントへのアクセスで発生していました。

調べると、これは Nginx のレート制限がトリガーされた結果でした。

自動スキャンがレート制限を踏んだ

インターネット上には、特定のフレームワークや外部サービスを使っているサーバーを自動で探し回るスキャンツールが常時動いています。

今回の503は、このブログには導入していない外部サービスのエンドポイントを探索するスキャンが、短時間に大量の POST リクエストを連続送信した結果、Nginx のレート制限に引っかかったものでした。

レート制限とは「同じ IP からのリクエストが一定数を超えたら弾く」という設定です。 ブルートフォース攻撃(総当たり攻撃)や過剰なアクセスからサーバーを守るために設けています。

レート制限が正常に機能した結果として503が返っていた、という意味では「正しく動いていた」のです。

ただ、503を返すことには別の問題がありました。

503は攻撃者に情報を渡している

503 Service Unavailable は、れっきとした HTTP レスポンスコードです。 HTTP のヘッダー情報ごとブラウザや自動ツールに返されます。

攻撃者の自動ツールはこのレスポンスを読んでいます。 503が返ってくると、ツールは以下のことを推測できます。

  • レート制限が存在する:503が返るということは、このサーバーはリクエスト数を監視している
  • 何かを処理するパスが存在する可能性がある:GET なら404が返るところ、POST だと503が返るなら、このパスは生きているかもしれない

攻撃者にとって「何も情報がない」状態と「レート制限があることがわかる」状態では、次の行動が変わります。 レート制限の存在を知ったツールは、リクエストの間隔を調整して再試行するように設計されていることがあります。

444 は何も返さない

Nginx には 444 という独自のステータスコードがあります。

444 は HTTP のレスポンスを一切返さず、TCP レベルで接続を即座に切断します。

| | 503 | 444 | |---|---|---| | HTTP レスポンス | 返す(ヘッダー含む) | 返さない | | 攻撃者への情報 | 「レート制限あり」が伝わる | 何も伝わらない | | サーバーの処理コスト | レスポンス送信処理が発生 | TCP 接続切断のみ | | 正規ユーザーへの影響 | リクエストが失敗する | リクエストが失敗する(同じ) |

攻撃者に「何も伝えない」ことはセキュリティの基本的な考え方のひとつです。 444 は攻撃者に何も教えず、サーバーの処理コストも最小にできます。

Nginx の設定変更

Nginx では limit_req_status というディレクティブで、レート制限超過時に返すコードを指定できます。

nginx
location /api/ { limit_req zone=api burst=15 nodelay; limit_req_status 444; # レート制限超過時に接続を即時切断 proxy_pass http://localhost:3000; }

limit_req_statushttp {}server {}location {} のどのブロックにも書けます。

どこに書くかで迷った

最初は server {} ブロックに書こうとしました。 そうすれば、そのバーチャルホスト全体に一括で適用できます。

ただ、私の設定ではレート制限のゾーンが2種類ありました。

  • /api/ への POST を対象にしたゾーン(今回503が出ていたもの)
  • サイト全体への POST を対象にしたゾーン(503は出ていない)

server {} ブロックに書くと、問題が起きていない方のゾーンにも変更が及んでしまいます。 問題が起きている場所だけを修正する、影響範囲を最小にする、という判断で、503が出ていた /api/location {} ブロックの中だけに追加しました。

nginx
location /api/ { limit_req zone=api burst=15 nodelay; limit_req_status 444; # ← ここだけに追加 proxy_pass http://localhost:3000; }

適用はダウンタイムなし

設定変更後は構文チェックをしてからリロードします。

bash
sudo nginx -t
bash
sudo nginx -s reload

sudo nginx -s reload はサーバーを再起動せずに設定だけを反映させるコマンドです。閲覧中のユーザーへの影響なく変更を適用できます。

メインブログの関連記事

Nginx のセキュリティ設定については、メインブログの第18回補足で詳しく解説しています。

【Next.jsブログ構築】VPSのセキュリティ強化とSQLite導入|fail2ban・Nginx設定手順

この記事が役に立ったら、シェアしていただけると嬉しいです!

関連記事