やりたかったこと
nginxで特定のURLへのリクエスト数を制限したかった。 APIエンドポイントへのブルートフォース攻撃や、過剰なリクエストをブロックするために設定してみた。
limit_req_zoneを設定する
nginx.conf(または /etc/nginx/conf.d/ 配下の設定ファイル)の http ブロックに追加する。
http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
}
$binary_remote_addr:クライアントのIPアドレスをキーにするzone=mylimit:10m:ゾーン名と共有メモリのサイズ(10MBで約16万IPを管理できる)rate=10r/s:1秒あたり10リクエストまで許可
r/m にすれば「1分あたり〇件」にもできる。
limit_req_zone $binary_remote_addr zone=loginlimit:10m rate=5r/m;
limit_reqでエンドポイントに適用する
server または location ブロックで制限を適用する。
server {
location /api/ {
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://localhost:3000;
}
}
burst=20:一時的に20リクエストまでのバーストを許可するnodelay:バーストしたリクエストを遅延なく処理し、超えたら即エラーを返す
nodelay を付けないと、超過したリクエストはキューに入って処理が遅延する。
ログインページに厳しめの制限をかける
ブルートフォース対策にはバーストを小さく設定するといい。
location /login {
limit_req zone=loginlimit burst=5;
proxy_pass http://localhost:3000;
}
レスポンスコードを503から429に変更する
デフォルトは503(Service Unavailable)だが、429(Too Many Requests)のほうがHTTPの仕様として正しい。
http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
limit_req_status 429;
}
設定を反映する
# 構文チェック
sudo nginx -t
# リロード(サービス停止なし)
sudo systemctl reload nginx
ハマったポイント
limit_req_zoneはhttpブロックに書く。serverやlocationに書くとエラーになるburstを設定しないと通常のユーザーも弾かれやすい。適切な値に調整が必要- レート制限に引っかかったリクエストはエラーログに記録される。
/var/log/nginx/error.logで確認できる - クライアントがNATやリバースプロキシの後ろにいる場合は
$remote_addrではなく$http_x_forwarded_forを使う必要がある場合もある nginx -tで確認してからreloadする。restartだと一瞬サービスが止まるので注意
関連記事
- nginxの基本的な設定ファイルの書き方
- nginxのlocationディレクティブの書き方と優先順位
- nginxのリバースプロキシ設定(Node.jsアプリをnginxで公開する)
- LinuxのUFWファイアウォール設定の基本
- nginxにLet’s EncryptのSSL証明書を設定する方法(certbot)
おすすめのVPS/ドメイン/スクール
VPSを使って本番環境を構築するなら、以下のサービスがおすすめです。