Appearance
REST API レート制限の IP アドレス取得設定
非同期読み込み REST(daily-article/v1 の AsyncLoadingHandler 全ルート)とパフォーマンス計測 REST(API-001-7)のレート制限では、クライアント IP をキーに制限をかけています。本ドキュメントは、対象ルートとバケット、IP 取得ロジックの設定方法、本番環境移行時の注意をまとめたものです。
対象ルートとバケット
WordPressAsyncLoadingPermissionChecker が nonce(wp_rest)を検証したうえで、IP ごとに 60 秒の固定ウィンドウで数えます。warm 系 POST は読み取り系とは別のカウンタです(Issue #3865)。値は core_src/Constants/AsyncLoadingRateLimit.php。
| バケット | ルート | method | 上限(60 秒・IP ごと) | カウンタを取れないとき |
|---|---|---|---|---|
読み取り(read) | /async-ranking(API-001-1) | GET | 300 回(全読み取りルートで共有) | 通す(可用性優先) |
読み取り(read) | /async-heatmap-detail(API-001-2) | GET | 同上 | 通す |
読み取り(read) | /async-heatmap-simple(API-001-9) | GET | 同上 | 通す |
読み取り(read) | /async-end-number(API-001-3) | GET | 同上 | 通す |
読み取り(read) | /async-kishu-search(API-001-4) | GET | 同上 | 通す |
読み取り(read) | /async-kishudata(API-001-14) | GET | 同上 | 通す |
読み取り(read) | /async-relational-day(API-001-11) | GET | 同上 | 通す |
読み取り(read) | /async-move-day-hall(API-001-12) | GET | 同上 | 通す |
読み取り(read) | /async-mail-image(API-001-13) | GET | 同上 | 通す |
読み取り(read) | /async-mail-image-batch(API-001-16) | GET | 同上 | 通す |
読み取り(read) | /async-move-day-hall-batch(API-001-17) | GET | 同上 | 通す |
読み取り(read) | /async-kishu-count-delta-list(SC-015) | GET | 同上 | 通す |
読み取り(read) | /period-kishu-samai-ranking(API-001-15) | GET | 同上 | 通す |
warm(warm) | /async-warm-template-data(API-001-10) | POST | 60 回(2 ルートで共有) | 拒否(429) |
warm(warm) | /async-warm-block-html(API-001-18) | POST | 同上 | 拒否(429) |
- 読み取り系の 300 回は、日別記事 1 ページ(3 ホール)の読み取り GET(バッチ 2 本 + ホールごと 5 断片 + 記事内の機種データ数で 20〜50 本程度)を 1 分に 6 ページ以上と、CGNAT 等で数人が 1 IP にまとまる場合の余裕を見込んだ値です(Issue #3482 / #3938)。
- warm 系はキャッシュを生成する重い処理で、1 ページ表示あたり最大 2 本(template-data / block-html)しか呼びません(ホール数では増えない)。遅延ブロックがすべてサーバー側で stitch 済みのページでは送りません。60 回は 30 ページ分です(旧 20 回は IP を共有するゲスト数人で 429 になったため広げた。Issue #3938)。warm が 429 になってもフロントは UI 用 REST を続けるため、表示は止まりません。
- カウンタを取れないとき(object cache の
wp_cache_add/wp_cache_incrの失敗、transient の保存失敗)は、読み取り系は表示を止めないために通し、warm 系だけ拒否します。全面 fail-closed にすると、キャッシュ障害時に日別記事の非同期表示がすべて止まるためです。 - 永続 object cache が無い環境では transient にフォールバックします(Issue #3464)。transient の値は
['start' => ウィンドウ開始時刻, 'count' => 回数]で、開始から 60 秒を過ぎると 1 から数え直します。TTL は加算のたびに延ばさず、ウィンドウの残り秒数(最低 1 秒)で保存するため、アクセスが続いても object cache と同じく最初のリクエストから 60 秒でリセットされます(Issue #3978)。旧形式(回数の整数だけ)の値が残っていた場合は、新しいウィンドウとして 1 から数えます。 - transient の get → 加算 → set は原子的でなく、並行リクエストでカウントが欠けることがある既知制約が残ります。並行リクエストが同じ値を先に保存すると
set_transientは false を返すため、そのときは保存済みの値を読み直し、同じウィンドウで書こうとした値以上ならその値で数えます。読めない・書こうとした値未満(保存されていない)ときは「カウンタを取れない」とみなします(warm 系の同時 2 本が誤って 429 にならないようにするため)。 - warm 系の 60 回は IP 単位です。プロキシ・CDN の背後で
USE_CLOUDFLARE_IP/TRUSTED_PROXY_HEADERが未設定だと全ユーザーが 1 つの枠を共有し、warm がすぐ 429 になってキャッシュ生成が進まなくなります(表示は止まらないが遅くなる)。本番の設定は下記「注意事項」を確認してください。 - バケットは
AsyncLoadingHandlerが permission の context(AsyncLoadingRateLimit::CONTEXT_BUCKET_KEY)に入れます。context はサーバー側で組み立てるため、クライアントがバケットを選ぶことはできません。
パフォーマンス計測 REST(/performance-measurement、API-001-7)は別の仕組み(BotRestRateLimiter)で、ユーザーごと 60 秒 10 回と IP ごと 60 秒 20 回を数えます。詳細は API-001-7。
前提
- 定数は 本プロジェクトでは定義しません。
wp-config.php(WordPress ルート)または環境変数で、本番サーバー側で定義します。 - 本プロジェクトから本番へデプロイするのは core_src 配下と connector.php のみです。
wp-config.phpは本プロジェクトに含まれず、本番サーバーの WordPress ルートで別管理となります。
デフォルトの挙動
- REMOTE_ADDR のみを使用します。
X-Forwarded-ForやX-Real-IPなど、クライアントが偽装可能なヘッダーは 参照しません(セキュリティのため)。
定数による設定
Cloudflare や信頼できるリバースプロキシの背後で運用する場合、wp-config.php で以下のいずれかを定義すると、該当ヘッダーから IP を取得します(取得できない場合は REMOTE_ADDR にフォールバック)。
両方の定数が定義されている場合は、USE_CLOUDFLARE_IP が優先され、HTTP_CF_CONNECTING_IP から取得できない場合に TRUSTED_PROXY_HEADER が使われます。
USE_CLOUDFLARE_IP
Cloudflare 経由でアクセスしている場合に、CF-Connecting-IP ヘッダーから IP を取得します。
php
// wp-config.php(WordPress ルート)
define( 'USE_CLOUDFLARE_IP', true );TRUSTED_PROXY_HEADER
信頼できるプロキシが設定する $_SERVER のキーを指定します。例: HTTP_CF_CONNECTING_IP, HTTP_X_FORWARDED_FOR(信頼できる環境のみ)。
php
// wp-config.php(WordPress ルート)
define( 'TRUSTED_PROXY_HEADER', 'HTTP_CF_CONNECTING_IP' );環境変数での指定(例)
サーバーやコンテナで環境変数を使う場合の例です。wp-config.php で読み込んでから定数として定義してください。
php
// wp-config.php
if ( getenv( 'USE_CLOUDFLARE_IP' ) === 'true' ) {
define( 'USE_CLOUDFLARE_IP', true );
}
if ( getenv( 'TRUSTED_PROXY_HEADER' ) !== false ) {
define( 'TRUSTED_PROXY_HEADER', getenv( 'TRUSTED_PROXY_HEADER' ) );
}本番環境移行時の注意
デプロイ対象
- 本プロジェクトのデプロイ対象は core_src 配下と connector.php のみです。
wp-config.phpは含まれず、本番サーバーの WordPress ルートで別管理です。
プロキシ・CDN 利用時
- 本番で Cloudflare やリバースプロキシを利用している場合、本番の wp-config.php に
USE_CLOUDFLARE_IPまたはTRUSTED_PROXY_HEADERを追加しないと、レート制限がプロキシの IP(REMOTE_ADDR)でかかります。 - その結果、同一プロキシ経由の全ユーザーで制限を共有するなど、想定外の挙動になる可能性があります。
- 本番で Cloudflare やリバースプロキシを利用している場合、本番の wp-config.php に
本番移行チェックリスト
- REST API レート制限で「クライアント単位」の制限を期待する場合は、本番 wp-config.php で上記定数を設定済みか確認することを推奨します。
- チェック例: 「REST API レート制限でプロキシ IP を使う場合は、本番 wp-config.php で定数を設定済みか」
関連
- 実装:
core_src/Infrastructure/word_press/RestClientIpResolver.php(IP 取得)、core_src/Infrastructure/word_press/WordPressAsyncLoadingPermissionChecker.php(非同期 REST の nonce・レート制限)、core_src/Infrastructure/word_press/WordPressPerformanceMeasurementPermissionChecker.php(パフォーマンス計測 REST)