Flask でレートリミットを実装する

API に対する過剰なリクエストを防ぐため、レートリミット(流量制限)を実装することは重要である。Flask-Limiter を使えば簡単にレートリミットを導入できる。

Flask-Limiter のセットアップ

pip install flask-limiter
from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
limiter = Limiter(
    key_func=get_remote_address,
    app=app,
    default_limits=['200 per day', '50 per hour']
)

key_func はリクエストを識別するキーを返す関数である。IP アドレスでの制限が一般的だが、認証済みユーザーの ID を使うこともできる。

エンドポイント単位の制限

@app.route('/api/search')
@limiter.limit('10 per minute')
def search():
    return {'results': []}

@app.route('/api/login', methods=['POST'])
@limiter.limit('5 per minute')
def login():
    return {'status': 'ok'}

制限の書式

10 per minute1分間に10リクエスト
100 per hour1時間に100リクエスト
1000 per day1日に1000リクエスト
1 per second1秒に1リクエスト

複数の制限を組み合わせることもできる。

@app.route('/api/data')
@limiter.limit('100 per hour')
@limiter.limit('10 per minute')
def data():
    return {'data': 'value'}

動的なキー関数

ログインユーザーごとに制限する場合。

def get_user_id():
    if hasattr(g, 'user') and g.user:
        return str(g.user.id)
    return get_remote_address()

limiter = Limiter(key_func=get_user_id, app=app)

特定のユーザーを除外

@app.route('/api/data')
@limiter.limit('10 per minute')
@limiter.exempt
def data():
    # このエンドポイントは制限なし
    return {'data': 'value'}

# または条件付きで除外
def whitelist_check():
    return g.user and g.user.is_admin

@app.route('/api/admin')
@limiter.limit('10 per minute', exempt_when=whitelist_check)
def admin():
    return {'admin': 'data'}

Redis をストレージに使う

デフォルトではメモリ内にカウントを保持するが、本番では Redis を使う。

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app.config['RATELIMIT_STORAGE_URI'] = 'redis://localhost:6379'

limiter = Limiter(
    key_func=get_remote_address,
    app=app,
    storage_uri='redis://localhost:6379'
)

エラーレスポンスのカスタマイズ

@app.errorhandler(429)
def ratelimit_error(e):
    return {
        'error': 'Too many requests',
        'retry_after': e.description
    }, 429

ヘッダーの確認

レートリミットの状態はレスポンスヘッダーで確認できる。

X-RateLimit-Limit制限数
X-RateLimit-Remaining残りリクエスト数
X-RateLimit-Resetリセット時刻
Retry-After再試行までの秒数(429 時)

レートリミットは API の安定性を保ち、悪意のあるリクエストからシステムを守るために重要である。