Vercel EdgeとUpstash RedisでNext.jsにレート制限を実装する方法
Vercel Edge Middlewareと@upstash/ratelimitライブラリを活用して、Webアプリケーションにレート制限(レートリミット)を実装する方法を解説します。@upstash/ratelimitは、バックエンドでRedisを使用してレート制限データの保存・管理を行うライブラリです。
Vercel Edgeを使うメリット
Vercel Edgeは、ユーザーに最も近いロケーションで計算処理を実行するコンピューティングプラットフォームです。本記事では、リクエストがバックエンドに到達する前に処理を割り込ませるVercel Edge Middlewareを使用します。レート制限の実装に最適な理由は以下の通りです。
- バックエンドから分離されているため、トラフィックがバックエンドに到達する前にエッジロケーションでブロックできます。
- 高速でコールドスタートの問題がないため、オーバーヘッドが最小限に抑えられます。
- サーバーレス関数と比較してコスト効率に優れています。
@upstash/ratelimitを選ぶ理由
- @upstash/ratelimitは、エッジ関数向けに特別に設計・テストされたレート制限ライブラリです。
- マルチリージョンRedisをサポートしており、エッジロケーションからの低レイテンシを実現します。
- Upstash Redisは、REST APIを備えているため、エッジ関数からアクセスできる唯一のマネージドRedisです。
それでは、実際の実装を見ていきましょう。
ステップ1:Redisのセットアップ
Upstash ConsoleまたはUpstash CLIでRedisデータベースを作成します。次のステップでUPSTASH_REDIS_REST_URLとUPSTASH_REDIS_REST_TOKENが必要になるので、控えておいてください。

ステップ2:Next.jsのセットアップ
まず、Next.jsアプリケーションを作成します(他のフレームワークをお使いの場合は公式ドキュメントをご確認ください)。
npx create-next-app@latest --typescript
続いて、@upstash/ratelimitをインストールします。
npm i @upstash/ratelimit
プロジェクトフォルダのトップレベルにmiddleware.tsを作成します。
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const redis = new Redis({
url: "https://us1-merry-snake-32728.upstash.io",
token: "AX_sAdsdfsgODM5ZjExZGEtMmmVjNmE345445kGVmZTk5MzQ=",
});
const ratelimit = new Ratelimit({
redis: redis,
limiter: Ratelimit.slidingWindow(5, "10 s"),
});
export default async function middleware(
request: NextRequest,
event: NextFetchEvent,
): Promise<Response | undefined> {
const ip = request.ip ?? "127.0.0.1";
const { success, pending, limit, reset, remaining } =
await ratelimit.limit(ip);
return success
? NextResponse.next()
: NextResponse.redirect(new URL("/blocked", request.url));
}
export const config = {
matcher: "/",
};
RedisのURLとトークンは、ご自身の環境のものに必ず置き換えてください。このコードではslidingWindowアルゴリズムを使用しており、同一IPアドレスからのリクエストを10秒間に5回まで許可しています。ユーザーIDやメールアドレスなど、ユーザー固有の識別子がある場合は、IPアドレスの代わりにそちらを使うことも可能です。レート制限に引っかかったリクエストは、middlewareによってblockedページへリダイレクトされます。
続いて、pages/blocked.tsxを作成しましょう。
import styles from "@/styles/Home.module.css";
export default function Blocked() {
return (
<div>
<main className={styles.main}>
<h3>Access blocked.</h3>
</main>
</div>
);
}
これで実装は完了です。次のコマンドでアプリをVercelにデプロイしましょう。
vercel deploy
デプロイ後、ページを何度か更新してみてください。制限回数に達した時点で、blockedページへリダイレクトされることを確認できます。
キャッシュによるリモート呼び出しの削減
すべてのリクエストに対してリモート呼び出しを行うのは非効率です。@upstash/ratelimitパッケージには、エッジ関数が「ホット」な状態である限りデータをキャッシュする便利な機能があります。つまり、関数が「コールド」な状態のときだけRedisからデータを取得する仕組みになっており、リモート呼び出しの回数を大幅に減らせます。キャッシュを有効にするには、ratelimitオブジェクトをハンドラー関数middlewareの外側で宣言します。
レート制限アルゴリズムの選択
上記の例ではスライディングウィンドウ(sliding window)アルゴリズムを使用しました。用途に応じて、固定ウィンドウ(fixed window)やリーンバケット(leaking bucket)など、他のアルゴリズムを選択することもできます。それぞれの特徴や使い分けについては、公式ドキュメントをご確認ください。
マルチリージョンRedisの活用
エッジ関数からのアクセス性能をさらに高めたい場合は、複数のリージョンにRedisデータベースを配置するのが有効です。マルチリージョンRedisの構成方法についても、公式ドキュメントで詳しく解説されています。
参考リンク
- Next.jsでのレート制限(非エッジ版)
- Vercel Edge Middleware
- Ratelimiting SDK
-
【Redis入門】SMEMBERSコマンドでセットの全要素を取得する方法
このチュートリアルでは、redis-cliでSMEMBERSコマンドを使用して、指定したキーに保存されたセット(Set)型の値からすべての要素を取得する方法を解説します。 SMEMBERSは、セットに格納されている全メンバーを一括で取得できるシンプルかつ便利なコマンドです。それでは、基本的な使い方を見ていきましょう。 構文 redis host:port> SMEMBERS <keyname> 戻り値 配列(Array):セットに含まれるすべての要素を返します。 エラー:キーが存在するものの、そのキーに保存されている値がセット型ではない場合にエラーを返します。 使用例 ま
-
QStashスケジューラでWhatsAppリマインダーを自動化する方法
QStashは、サーバーレス環境向けに設計されたタスクスケジューラ兼メッセージキューのシステムです。状態管理を意識することなく、特定の間隔で1回だけ、あるいは繰り返し実行される自動化ワークフローを構築できるため、開発者にとって非常に心強いツールとなっています。 まずはQStashのコンソールを実際に触ってみて、リクエストの組み立て方を感覚的に掴んでみると良いでしょう。 ユースケース 私が開発しているプロダクト「GPT Hotline」は、いわばWhatsApp上で動くChatGPTです(同じ基盤モデルを使用しています)。ChatGPTアプリを使うときと同じ感覚で、専用の番号宛てにボットへメッ