はじめに
本記事では前回までの認証付き Hono アプリに、Rate Limiting(レート制限)を導入する方法を紹介します。
レート制限をかけることで、短時間で大量のリクエストが飛んできた場合にエラーを返し、サービスの安定稼働を図れるようにします。
1. レート制限の概要
短時間であまりに多くのリクエストが発生すると、サーバーは負荷が高まり、他の正常なユーザーのリクエストに応答できなくなる恐れがあります。
そこで一定時間内に処理するリクエスト数を制限し、しきい値を超えた場合はエラーレスポンスを返却する仕組みを導入します。
2. 全体フローイメージ
3. インストール
Rate Limiting を実装するためのライブラリは複数ありますが、ここではシンプルに「メモリ上でリクエスト回数をカウントするだけ」の例を示します。
本番運用では分散環境に対応できるよう Redis などを使う場合が多いです。
4. ディレクトリ構成
my-hono-crud/
├─ .env
├─ package.json
├─ tsconfig.json
├─ src/
│ ├─ index.ts
│ ├─ routes/
│ │ └─ auth.ts
│ ├─ middlewares/
│ ├─ auth.ts
│ └─ rateLimit.ts // 今回追加
│ └─ users/
│ └─ index.ts
└─ ...
5. レート制限ミドルウェアの実装例
5-1. src/middlewares/rateLimit.ts
import { Context, Next } from 'hono'
// IPごとの履歴を保持するためのマップ
const requestHistory = new Map<string, number[]>()
// 1分間に許容するリクエスト数
const LIMIT = 10
// しきい値時間 (ミリ秒)
const TIME_WINDOW = 60 * 1000
export const rateLimitMiddleware = async (c: Context, next: Next) => {
// IPアドレス取得(プロキシ環境やCloudflare環境に対応)
const ip = c.req.header('x-forwarded-for') ||
c.req.header('CF-Connecting-IP') ||
c.req.header('X-Real-IP') ||
c.env?.remoteAddr || // Cloudflare Workers環境用
'unknown'
const now = Date.now()
const timestamps = requestHistory.get(ip) || []
// 古いタイムスタンプを除去
const recentTimestamps = timestamps.filter((ts: number) => now - ts < TIME_WINDOW)
// 今回のアクセスを追加
recentTimestamps.push(now)
requestHistory.set(ip, recentTimestamps)
// LIMITを超えていれば429エラーを返却
if (recentTimestamps.length > LIMIT) {
return c.text('Too Many Requests', 429)
}
await next()
}
5-2. src/index.ts への組み込み
import { Hono } from 'hono'
import { loginHandler } from './routes/auth'
import { rateLimitMiddleware } from './middlewares/rateLimit'
import userApp from './users/index'
import 'dotenv/config'
const app = new Hono()
// レート制限ミドルウェアを全ルートで使用
app.use('*', rateLimitMiddleware)
// ログイン用エンドポイント
app.post('/login', loginHandler)
// ユーザー CRUD エンドポイント
app.route('/users', userApp)
export default app
6. 動作確認
# 連続で11回叩いてみる例
for i in {1..11}; do
curl http://localhost:8787
done
7. まとめと応用
- レート制限 は高負荷攻撃やスパムなどを防ぐうえで重要な施策です。
- 本番環境では Redis などを利用して スケーラブル な運用を検討しましょう。
- IP アドレス 以外にも ユーザーID や JWT トークン 単位でのレート制限も可能です。