この記事は、Cloudflare上でAIプロダクトを運用する中で見つけた落とし穴シリーズの1本である。レート制限とログインロックアウトを、なりすまし可能なヘッダーから信頼できるはずのヘッダーに切り替える修正を検討したところ、本番の経路にCloudflareが挟まっていることを見落としていた、という話をまとめる。
この記事で分かること
- Cloudflare配下でX-Real-IPをそのまま信頼すると何が起きるか
- 正しい実クライアントIPの取り方
- 断定的に検証する方法
この記事で分からないこと(正直に書く)
- 修正前の設定がどれだけの期間本番で稼働していたかは、本記事の材料からは分からない
何が起きたか
カンタン補助金のログインロックアウトとレート制限のクライアントIP判定を、先頭要素がなりすまし可能なX-Forwarded-Forから、X-Real-IPベースに切り替える修正を検討した。アプリケーションサーバーの手前にあるnginxの設定には実クライアントのIPを転送する指定がすでにあり、これなら実クライアントのIPが入ると判断した(出所: 自社の運用記録、確認日2026-09-06)。
なぜ気づけなかったか
設定ファイルの記述だけを見ると、この値は接続元のIPであり一見正しく見える。しかし本番の経路は「Cloudflare→nginx→アプリケーションサーバー」であり、nginxからみた接続元は実際のブラウザではなくCloudflareのエッジサーバーになる。つまりX-Real-IPに入るのは実クライアントのIPではなく、Cloudflareのエッジ側のIPだった。多数の実ユーザーが同じエッジを経由するため、そのままデプロイしていれば、IP単位のレート制限が複数ユーザーで共有されるバケットになり、無関係なユーザーが誤って制限にかかる可能性が高い。ログインロックアウトも他人の失敗回数を巻き込むリグレッションになるところだった。設定ファイルというコードの末端だけを見て、手前にCloudflareが挟まっている経路全体を見ていなかったことが原因である。
どう直したか
nginxのrealipモジュールで、Cloudflareの公開IPレンジから来た接続だけ、送信元IPをCF-Connecting-IPの値に差し替える設定に直した。Cloudflareの公式ドキュメントでも、CF-Connecting-IPは元の訪問者のIPアドレスを保持するリクエストヘッダーとして説明されており、オリジン側での復元方法も案内されている(https://developers.cloudflare.com/fundamentals/reference/http-headers/, 確認日2026-09-06)。信頼するIPレンジをCloudflare公式の一覧に限定しているため、レンジ外からの偽装では上書きが発生しない。検証は、目印付きUser-AgentでCloudflare経由のリクエストを送り、アクセスログの接続元が自分自身の実際の送信元IPになっていることを確認する方法で行った。
同じ構成の製品で先に見る場所
自社はカンタン補助金のほか複数のプロダクトを「Cloudflare→アプリケーションサーバー」の構成で運用している。同じ構成のプロダクトでは、レート制限やログイン制限のIP判定ロジックが何のヘッダーを見ているかをまず確認し、次にそのヘッダーが実クライアントIPに復元される設定になっているかを、送信元レンジの限定まで含めて確認するのが最初の一手になる。設定ファイルの記述だけでなく、経路の最前段から順に追う必要がある。
見えている値が誰の値かを確認しないまま対応を決めると別の障害を招く、という点は504がEarly Hintsだった記事やDurable ObjectsのSQLite無料枠の記事にも近い教訓がある。関連してRAGの保管費用の記事も参照してほしい。
自社のCloudflare運用・アクセス制御について相談したい場合は、お問い合わせから編集部まで連絡してほしい。
よくある質問
Q1. Cloudflare配下でX-Real-IPをそのまま信頼するとどうなるか
多数の実ユーザーが同じCloudflareエッジ経由のIPを共有してしまい、IP単位のレート制限やログインロックアウトが無関係なユーザーを巻き込む形で誤爆する。
Q2. なぜnginxの設定ファイルを見ただけでは気づけなかったのか
設定ファイルという経路の末端だけを見て、その手前にCloudflareが挟まっているという経路全体を確認していなかったため。
Q3. 正しい実クライアントIPはどう取ればよいか
nginxのrealipモジュールで、Cloudflareの公開IPレンジから来た接続だけCF-Connecting-IPの値に送信元IPを差し替える。