レートを制限する
nginxのlimit_reqは、すべてのワーカーが使う共有メモリのゾーンで、キーごとにリクエストを数え、レートを超えたリクエストを拒否します。キーには、nginxのどの変数でも使えます。Rubyのスクリプトがmruby_setでキーを計算すれば、APIキーのような、Rubyがリクエストから取り出した値ごとに制限をかけられます。カウンタはnginxが持ちます。
スクリプトは、リクエストヘッダX-API-KeyのAPIキーを返します。ヘッダの無いリクエストではクライアントのアドレスを返します。こうして、どのリクエストもキーが空になりません。limit_reqは、キーが空のリクエストを数えないからです。ゾーンは、キーごとに1分に1リクエストを許します。nginxは、limit_req_status 429とerror_page 429 @limitedに従って、拒否したリクエストのレスポンスをステータス429とJSONのボディにします。
limit_req_zone $api_key zone=api_keys:1m rate=1r/m;
server { listen 18166;
location /api/ { mruby_set $api_key build/nginx/html/guide_rate_limit.rb; limit_req zone=api_keys; limit_req_status 429; error_page 429 @limited; mruby_content_handler_code 'Nginx.rputs "accepted\n"'; }
location @limited { default_type application/json; return 429 '{"error":"rate_limited"}\n'; }}r = Nginx::Request.newkey = r.headers_in["X-API-Key"].to_skey.empty? ? "addr:#{r.var.remote_addr}" : "key:#{key}"GET /api/orders HTTP/1.1Host: 127.0.0.1:18166X-API-Key: demo-keyConnection: closeHTTP/1.1 429 Too Many RequestsContent-Type: application/json
{"error":"rate_limited"}CIが動くたびに、ケースのtest/t/cases/guides.rbがこのリクエストを送ります。ケースは、ステータス行、ここに載せたヘッダ、ボディ全体を比べます。5個のファイルを最後に変えたのは、2026-10-29のコミット32219e53です。
ケースはリクエストを2回送ります。1回目のレスポンスは、ボディがacceptedの200です。同じ1分のうちに送った2回目のリクエストには、上のレスポンスが返ります。
気をつけること
Section titled “気をつけること”mruby_setはrewriteフェーズで動き、limit_reqはその後のpreaccessフェーズで動きます。そのため、nginxがリクエストを数えるときには、変数に値が入っています。フェーズの順はリクエストのフェーズにあります。- カウンタは、Rubyではなくnginxの共有メモリのゾーンにあります。各ワーカーは自分の
mrb_stateを持つので、Rubyのグローバル変数のカウンタでは、1つのワーカーのリクエストしか数えられません。 limit_req_zoneはhttpブロックのディレクティブです。limit_reqとlimit_req_statusは、httpブロック、server、locationに書けます。