レート制限
1 人の読者がアシスタント、API プレイグラウンドのプロキシ、サーバーサイド検索を呼び出せる頻度を制限します。デフォルトで有効で、カウントはメモリ、Upstash、Cloudflare、Unkey のいずれかに保持されます。
読者が呼び出せるサーバールートは、呼び出されるたびにコストが発生します。アシスタントはモデルのトークンを消費し、API プレイグラウンドのプロキシはリクエストをお使いの API に中継し、Mixedbread 検索は有料のクエリを実行します。Blume は、IP アドレスで識別される 1 人の読者がそれぞれのルートを呼び出せる頻度を制限します。制限を超えた読者には Retry-After ヘッダー付きの 429 Too Many Requests が返され、アシスタントは数分後にもう一度試すよう読者に伝えます。
レート制限はデフォルトで有効で、読者 1 人・ルートごとに 10 分あたり 30 リクエストです。人がアシスタントやプレイグラウンドを普通に使う分には上限に達することはなく、スクリプトを止めるには十分に低い値です。検索はカウントのされ方が異なります。検索ダイアログは読者が入力中に手を止めるたびにクエリを送信するため、1 回の検索で複数のリクエストが発生することがあり、数分間続けて検索する読者は上限に達する可能性があります。サイトで Mixedbread 検索を使用している場合は、requests を引き上げてください。この値はすべてのルートに適用されます。レート制限が関係するのはサーバービルドの場合のみで、静的サイトには制限対象となるサーバールートがありません。
import { defineConfig } from "blume";
import { upstash } from "blume/ratelimit";
export default defineConfig({
rateLimit: upstash({ requests: 30, window: 600 }), // window in seconds
});
各ルートはそれぞれ独自のカウントを保持するため、プレイグラウンドを頻繁に使っても、その読者がアシスタントに質問できる回数が減ることはありません。
カウントの保存場所
| アダプター | カウントの保存先 | 制限の精度 |
|---|---|---|
memory() |
サーバーのメモリ(デフォルト) | 単一サーバーでは正確。サーバーレスホストではインスタンスごと |
upstash() |
Upstash Redis | すべてのホストで正確 |
cloudflare() |
Cloudflare の Workers レート制限 | Cloudflare のロケーションごと |
unkey() |
Unkey | すべてのホストで有効。リージョン間のカウントはすぐに同期 |
メモリ
memory() には何の準備も必要ありません。deployment: node() のように単一プロセスとして動作するサーバーでは、カウントは正確です。Vercel、Netlify、Cloudflare では短時間だけ動作するインスタンスが多数実行され、それぞれが独自のカウントを保持します。そのため、これらのホストでは制限を正確に適用するというよりも、1 人の読者からの集中的なリクエストを食い止める役割になります。これらのホストで制限を正確に適用するには、以下の共有ストアのいずれかを使用してください。
import { memory } from "blume/ratelimit";
rateLimit: memory({ requests: 60, window: 600 }),
Upstash
upstash() は、すべてのインスタンスで共有される Upstash Redis にカウントを保持します。データベースを作成し、その REST エンドポイントとトークンを UPSTASH_REDIS_REST_URL と UPSTASH_REDIS_REST_TOKEN に設定してください。Blume は Upstash の REST API と直接通信するため、インストールが必要なものはありません。両方が設定されるまでは、ルートはメモリでカウントし、blume build が警告を表示します。
import { upstash } from "blume/ratelimit";
rateLimit: upstash(),
Vercel の Marketplace から追加したデータベースでは、エンドポイントとトークンが代わりに KV_REST_API_URL と KV_REST_API_TOKEN として設定されます(接続時にプレフィックスを選んだ場合は、それが付きます)。urlEnv と tokenEnv で変数名を指定してください。
rateLimit: upstash({ urlEnv: "KV_REST_API_URL", tokenEnv: "KV_REST_API_TOKEN" }),
Cloudflare
cloudflare() は、deployment: cloudflare() でデプロイされたサイト向けに、Workers レート制限を使ってカウントします。Blume はビルド時に Worker の設定でバインディングを宣言するため、セットアップは不要です。Cloudflare はロケーションごとにカウントし、ウィンドウとして 10 秒または 60 秒しか受け付けないため、デフォルトは 60 秒あたり 10 リクエストになっています。
import { cloudflare } from "blume/deploy";
import { cloudflare as cloudflareRateLimit } from "blume/ratelimit";
export default defineConfig({
deployment: cloudflare(),
rateLimit: cloudflareRateLimit({ requests: 10, window: 60 }),
});
バインディングの名前空間は Worker の名前から決まるため、同じアカウント上の 2 つのサイトがカウントを共有することはありません。名前空間を自分で指定するには、namespaceId を設定してください。
Unkey
unkey() は、すべてのインスタンスで共有される Unkey のレート制限にカウントを保持します。ratelimit.*.limit 権限に加えて、最初のリクエスト時に Unkey が名前空間を作成できるよう ratelimit.*.create_namespace 権限を持つルートキーを作成し、UNKEY_ROOT_KEY に設定してください。Blume は Unkey の REST API と直接通信するため、インストールが必要なものはありません。キーが設定されるまでは、ルートはメモリでカウントし、blume build が警告を表示します。
import { unkey } from "blume/ratelimit";
rateLimit: unkey(),
namespace で別の名前を指定しない限り、名前空間は docs になります。キーを保持する変数の名前は rootKeyEnv で指定してください。
rateLimit: unkey({ namespace: "acme-docs", rootKeyEnv: "DOCS_UNKEY_ROOT_KEY" }),
Unkey はリージョンごとにカウントし、リージョン間ではすぐにカウントを共有します。そのため、複数のリージョンに分散した短時間の集中的なリクエストでは、上限より数件多くのリクエストが通過することがあります。上記の権限が不足しているキーなど、Unkey が拒否するキーではすべてのカウントが失敗します。また、共有ストアがカウントに失敗したリクエストは通過します。そのため、サーバーのログに Rate limiting failed と記録された場合は、キーを確認してください。
リバースプロキシの背後に置く場合
Vercel、Netlify、Cloudflare は各読者のアドレスを Blume に伝えます。nginx や Caddy などのリバースプロキシの背後にある node() サーバーからは、すべてのリクエストがプロキシから来ているように見えます。そのため、すべての読者が 1 つのカウントを共有することになり、アクセスの多いサイトでは全員が上限に達してしまいます。プロキシが応答するホストを allowedDomains に指定してください。
import { node } from "blume/deploy";
deployment: node({
site: "https://docs.example.com",
allowedDomains: [{ hostname: "docs.example.com" }],
}),
これは Astro の security.allowedDomains です。Host または X-Forwarded-Host ヘッダーがいずれかのエントリーに一致するリクエストについて、Astro は X-Forwarded-For の最初のエントリーから読者のアドレスを取得します。プロキシは、サイトのホストをそのまま渡し、読者が送信した X-Forwarded-For をプロキシ自身が確認したアドレスで置き換えるように設定してください。nginx の proxy_set_header X-Forwarded-For $remote_addr; がその例です。置き換えずにヘッダーへ追記するプロキシでは、読者が自分のアドレスを自由に指定でき、リクエストのたびに新しいカウントを得られてしまいます。読者がサーバーに直接アクセスする場合は、allowedDomains を設定しないでください。
無効にする
たとえばホストのファイアウォールがすでにこれらのルートを制限している場合など、すべてのリクエストを通過させたいときは rateLimit: false を設定します。ホストがアドレスを判別できないリクエストは常に通過します。共有ストアがカウントに失敗したリクエストも同様です。この失敗はログに記録され、ストアの障害によって読者が締め出されることはありません。
アシスタントについては、多数のアドレスにリクエストを分散させるスクリプトを阻止するため、レート制限とボットチェックを組み合わせて使用してください。