ブログに戻る
2025年10月27日Sergei Solod7 分で読めます

静的な Next.js サイトには毎日数千件の脆弱性スキャンが来る。それでも Nginx はほとんど動じない

ログには WordPress、PHP バックドア、.env、.git/config を探すリクエストが大量に残ります。しかし静的な Next.js + Nginx 構成では、その多くが低コストなミスと 404 で終わり、観測中の CPU 負荷も変わりませんでした。静的サイトの安全性について、この経験から言えることと言えないことを整理します。

セキュリティNext.jsNginxDevOps静的ホスティングAttack Surface

私のサーバーログには、毎日数千件の自動脆弱性スキャンが記録されています。見た目だけならかなり物騒です。

  • /wp-admin/wp.php
  • /111.php
  • /code.php
  • /.git/config
  • /i.php
  • /admin
  • /api/.env
  • /kal.php

私が驚いたのは件数そのものではなく、サーバー側の反応でした。このトラフィックを観察している間、CPU 負荷には目立った変化がありませんでした。

この観測自体は事実ですが、以前の私はそこから少し強すぎる結論を出していました。静的配信は魔法の防壁ではなく、404 もコストゼロではありません。私の構成で本当に効いているのは、もっと限定的で実用的な点です。偶発的なスキャンの多くは、到達できるサーバーサイドのアプリケーションロジックがほとんどないため、処理コストが小さく済みます。

これは「侵入の証拠」ではなく、まずは探索リクエスト

/wp-admin/wp.php/111.php を見ると、すべてを「exploit」と呼びたくなります。しかし厳密には、私が主に見ているのは自動化された probing です。ボットがよくあるファイル名や endpoint を順番に試し、何が応答するかを確認しています。

WordPress や PHP を明確に狙ったパスもあります。一方、/.git/config/api/.env は、設定ファイルや秘密情報が誤って公開されていないかを探すものです。/admin はさらに一般的で、正規のルートとして存在するサイトもあります。怪しいリクエストから分かるのは「スキャナーが何を探しているか」であって、「その脆弱性が実在すること」ではありません。

私のケースで重要なのは構成です。公開サイトは Next.js で事前生成した静的 HTML で、Nginx から直接配信しています。

この構成では、なぜスキャン処理が軽いのか

ここで扱っているページの request path には、PHP runtime も、リクエストごとに呼ばれるデータベースもありません。Nginx はすでに存在するファイルを配信します。スキャナーが /111.php を要求しても、そのファイルが存在しなければ、PHP アプリケーションが起動することも、WordPress のコードが実行されることも、DB クエリが走ることもありません。単なる miss と 404 で終わります。

これによって 1 リクエストあたりのコスト構造が変わります。動的アプリケーションなら、routing、framework runtime、認証判定、テンプレート生成、DB アクセス、外部サービス呼び出しまで進む可能性があります。私の静的な配信経路には、そのような可動部分がずっと少ない。もちろん Nginx も接続を受け、HTTP を処理し、リソースを探し、ログを書き、レスポンスを返します。それでもアプリケーション側の仕事量は小さくなります。

サイトでは aggressive caching も使っています。通常の静的コンテンツ配信には有効ですが、ランダムな 404 すべてをキャッシュのおかげだとは考えていません。初めて見る無効な URI なら、404 を返す前に通常の Nginx 処理や filesystem lookup が発生し得ます。

重要な修正点:静的サイトは「定義上安全」ではない

今ならここを最も慎重に書きます。静的構成は公開 request path から多くのサーバーサイド部品を取り除きますが、セキュリティそのものを不要にはしません。

  • 公開されたファイルは、静的でも公開されたままです。 .git.env、バックアップ、秘密情報などを誤って public directory に置けば、設定で防いでいない限り Nginx は HTML と同じように配信できます。
  • Nginx、TLS、OS、ネットワークは残ります。 これらも attack surface の一部であり、更新と適切な設定が必要です。
  • クライアント側の脆弱性も残ります。 静的 HTML だからといって、JavaScript、ブラウザ側の認証ロジック、third-party script が自動的に安全になるわけではありません。
  • resource exhaustion も可能です。 connection、bandwidth、TLS handshake、worker capacity、ログ I/O、disk space には上限があります。十分な量のトラフィックなら静的サイトにも影響します。

したがって正確な結論は「これらのリクエストは無意味」ではありません。この構成では、多くのアプリケーション固有の probe に実行させる対象がない、ということです。

「1 日数千件」という数字は、見た目ほど負荷を説明しない

ログでは失敗した probe も 1 件ずつ見えるため、Internet の background noise が非常に激しく見えます。しかし 1 日の総数だけでは、運用上の影響は判断できません。数千件が 24 時間に分散する場合と、短時間に集中する場合では意味がまったく違います。

CPU も指標の一つにすぎません。私の観測では CPU 負荷は変わらず、このトラフィックがその時点で目立った計算負荷を作っていなかった、という証拠にはなります。ただし他のコストがゼロだったことまでは証明しません。本当に影響を評価するなら、request rate、bandwidth、active connections、Nginx worker の状態、response latency、log volume、disk usage も確認します。

ログを見続けて学んだこと

私にとって最大の学びは、アーキテクチャそのものが、個別の scanner signature を一つずつブロックしなくても、ある種の自動攻撃を成立しにくくできることです。ボットが一日中 WordPress や PHP backdoor を探しても、公開経路に WordPress も PHP も存在しなければ、その probe が利用できるものはほとんどありません。

ただし私は今、「この stack には関係が薄い」と「無害なトラフィック」を同じ意味では扱いません。現在の判断基準は次のようにしています。

  • 公開 request path から実際に到達できる runtime と service を把握する。
  • secret、repository metadata、backup、build artifact を web root の外に置くか、明示的にブロックする。
  • 他の兆候がない限り、繰り返される 404 probe は Internet の通常の background noise として扱う。
  • abusive traffic のコストを CPU だけで判断しない。
  • アプリケーションが静的でも、Nginx、OS、TLS library、dependency は更新し続ける。

この経験からは証明できないこと

私は controlled benchmark を行っていませんし、DDoS を再現したわけでもありません。production log と server load を見ながら、毎日数千件の scanner request が来ている状況を観察しただけです。自分の構成について実用的な結論を出すには十分ですが、「静的サイトは常に勝つ」という一般法則の証明にはなりません。

私が確実に言えるのは、より限定された事実です。Nginx が配信する私の Next.js static export では、観測した scanner traffic の多くが、PHP runtime や database-backed application に到達せず、低コストな file miss と 404 で終わりました。そして観測中の CPU 負荷は変わりませんでした。

私が静的構成に感じている resilience の価値はここです。部品が少ないから無敵になるわけではありません。ただ、ランダムな Internet noise が「本物のアプリケーション処理」に変わる場所を減らせます。