Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Protect Your Node.js API: Rate Limiting with Fixed Window, Sliding Window, and Token Bucket

2025年09月10日に「Dev.to」が公開したITニュース「Protect Your Node.js API: Rate Limiting with Fixed Window, Sliding Window, and Token Bucket」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

レートリミットは、APIへのリクエスト数を制限し、サーバーへの過負荷やDoS攻撃を防ぎ、誰もが公平にサービスを使えるようにする技術だ。固定ウィンドウ、スライディングウィンドウ、トークンバケットといった方式があり、システムの特性に応じて使い分ける。

ITニュース解説

システムを開発する上で、私たちが作ったアプリケーションやAPI(アプリケーション・プログラミング・インターフェース)は、外部からのアクセスを受け付ける窓口のようなものだ。この窓口が無制限に開かれていると、様々な問題が発生する可能性がある。その問題を未然に防ぎ、システムを安定稼働させるための重要な仕組みの一つが「レートリミット」である。レートリミットとは、簡単に言えば、あるユーザーやクライアントが一定の時間内にシステムに対して行えるリクエストの数を制限する戦略のことだ。例えば、「1分間に100回まで」といった具体的なルールを設定する。

では、なぜレートリミットがそこまで重要なのか。これには主に三つの理由がある。一つ目は「システムのリソースを不正利用から保護する」ためだ。もしレートリミットがなければ、悪意のあるユーザーや、誤って大量のリクエストを送信してしまうプログラムによって、サーバーは瞬く間にパンクしてしまうだろう。サーバーが処理しきれないほどのリクエストを受けると、システムがダウンしたり、他の正常なユーザーの動作が遅くなったりする。また、クラウドサービスを利用している場合、リクエスト数や計算時間に応じて費用が発生するため、無制限なリクエストはコストの増加にも直結する。レートリミットは、このような事態を防ぎ、システムが限られたリソース内で安定して動作するように守ってくれる。

二つ目は「サービス拒否(DoS)攻撃を防ぐ」役割だ。DoS攻撃とは、ハッカーが悪意を持って大量のリクエストを送りつけ、システムを麻痺させたり、停止させたりする攻撃のことである。レートリミットは、このような攻撃の初期段階で異常なリクエストをブロックし、サーバーの帯域幅、メモリ、CPUといった重要なリソースが消費し尽くされるのを防ぐ。これは、大規模な分散型DoS攻撃(DDoS攻撃)に対する完全な解決策ではないが、最初の防衛線として非常に効果的である。

三つ目は「公平な利用を強制する」ためだ。多くのユーザーが同時にシステムを利用する場合、一部のユーザーが大量のリクエストを送り、他のユーザーのアクセスを妨げてしまうことがある。例えば、公共のAPIサービスで「1分間に100リクエスト」という制限があれば、すべてのユーザーが同じチャンスを得られ、特定のユーザーがサービスを独占するのを防ぎ、公平なアクセスを保証できる。これにより、すべてのユーザーが快適にサービスを利用できるようになる。

このレートリミットを実現するための主要な戦略はいくつかあり、ここでは特に「固定ウィンドウ」「スライディングウィンドウ」「トークンバケット」の三つの人気アプローチについて解説する。

まず「固定ウィンドウ」戦略は、その名の通り、厳密に区切られた時間枠内でリクエスト数をカウントする方法だ。例えば、「1分間に5リクエストまで」と設定した場合、毎分0秒から59秒までの間で5回までしかリクエストできない。そして、分が変わると(つまり、次の時間枠に入ると)、リクエストのカウントは自動的にリセットされ、また5回までリクエストできるようになる。この方法は、実装が非常にシンプルで分かりやすいという利点がある。例えば、プログラムではユーザーのIPアドレスごとに「リクエスト数」と「時間枠の開始時刻」を記録し、現在の時間が時間枠の範囲内であればリクエスト数を増やし、超えていればリクエストを拒否するか、新しい時間枠としてリセットするといった処理を行う。小規模なプロジェクトや、複雑なロジックを避けたい場合に適しているが、時間枠の境界線で短時間に大量のリクエストが集中してしまう「バースト問題」が発生しやすいという欠点もある。例えば、毎分59秒に5リクエスト、次の1分0秒に5リクエストと短時間で立て続けにリクエストされた場合、合計10リクエストを実質2秒間で処理することになり、システムの負荷が高まる可能性がある。

次に「スライディングウィンドウ」戦略は、固定ウィンドウの持つバースト問題を解決するために考案された、より洗練された方法である。この戦略では、時間枠が固定されているのではなく、常に「現在時刻から過去一定時間(例えば過去1分間)」を監視対象とする。つまり、時間枠が常に現在時刻に合わせてスライドしていくようなイメージだ。例えば、「過去1分間に100リクエストまで」という制限の場合、ユーザーが90リクエストを過去50秒以内に行ったとすると、残りの10秒間ではあと10リクエストしか送れない。この方法は、より公平なリクエストの分散を可能にし、短時間での過度な集中を緩和できる。具体的な実装では、各リクエストが到着した「時刻」を記録し、新しいリクエストが来るたびに、記録されたリクエストの中から監視対象の時間枠(例えば過去60秒間)を外れた古いリクエストを削除し、残りのリクエスト数をカウントする。この処理を効率的に行うためには、Redisのような高速なデータベースがよく使われる。これにより、複数サーバーにまたがるシステムでも、ユーザーごとのリクエスト履歴を正確に管理できる。

最後に「トークンバケット」または「リーキーバケット」戦略は、最も柔軟で複雑なアプローチであり、突発的な大量リクエスト(バースト)をある程度許容しながら、全体的な処理レートを一定に保つことに優れている。この戦略は、「トークン(仮想的な許可証)」と「バケット(トークンを貯める容器)」という概念で説明できる。システムは一定の速度でバケットにトークンを補充し続ける。ユーザーがリクエストを送るには、バケットからトークンを一つ消費する必要がある。バケットにトークンがあればリクエストは許可され、トークンがなければリクエストは拒否されるか、待機させられる。バケットには容量があり、満杯になるとそれ以上トークンは補充されない。これにより、瞬間的には多くのトークンが消費されて大量のリクエストが処理される(バーストを許容する)が、バケットが空になると、次にトークンが補充されるまでリクエストは制限され、結果として平均的な処理レートが平滑化される。リーキーバケットも似た概念で、リクエストをバケット(キュー)に入れ、バケットから一定の速度でリクエストを「漏れ出させる」(処理する)というイメージだ。バケットがいっぱいになると、それ以上リクエストは受け付けられず、溢れたリクエストは破棄される。この戦略は、システムの負荷変動に柔軟に対応しつつ、安定したパフォーマンスを維持したい本番環境のシステムに最適である。

これらの戦略はそれぞれ異なる特性を持つため、プロジェクトの要件やトラフィックパターンに応じて適切なものを選ぶことが重要だ。固定ウィンドウはシンプルで実装しやすく、小規模なプロジェクトに適している。スライディングウィンドウはより公平なリクエスト配分を提供し、安定した負荷のAPIに効果的である。そしてトークンバケット/リーキーバケットは、突発的なリクエストに対応しつつ全体的な処理を平滑化するため、最も柔軟で本番環境での利用に向いている。

レートリミットは、単なるパフォーマンスを向上させるテクニックにとどまらず、不正利用やシステムダウン、不公平な利用からAPIを保護するための不可欠なガードレールである。これらの戦略を理解し、適切に導入することで、あなたのシステムはより堅牢で信頼性の高いものになるだろう。