Nginx limit_req 限制连接数用法示例
介绍一个 limit_req
经典配置方法,通过结合 map
使用,可以简化 Nginx 配置(实现在 server
块内添加 limit_req
限制,但仅作用于指定 URL。虽然也可以在 location
块里添加 limit_req
限制,但对于 php 资源的 location
块需要同时指定 fastcgi 配置,看起来略麻烦不够简洁)。
下面示例里的 map
之所以不用 $request_uri
是为防范含有重复 /
字符的不规范 URL 导致匹配失败逃逸限制,而 $uri
会去掉 URL 中的不规范字符。
limit_req 示例配置一
这个示例可以限制多个指定 URL,但每个 URL 都应用相同的限制速率。
限制效果:限制 WordPress 搜索/登录/注册/重置密码 URL 的请求数,每个 IP 每秒最多请求 5 次(均速处理,即每 200 毫秒处理 1 个请求),额外允许 10 个突发请求(即开始 200 毫秒实际允许处理 11 个请求,过后降至每 200 毫秒处理 2 个请求,因为突发名额每 200 毫秒释放 1 个),超出请求返回 429 错误。
http { ... map $uri$is_args$args $limit_req_level { default ""; ~\?(?:.*&)?s= $binary_remote_addr; ~^/wp-login\.php $binary_remote_addr; } limit_req_zone $limit_req_level zone=req_general:10m rate=5r/s; limit_req_status 429; ... server { ... limit_req zone=req_general burst=10 nodelay; ... } }
limit_req 示例配置二
这个示例与上个的区别是可以对不同 URL 应用不同的限制速率,并且添加了全局限制(如不需要,删除其中创建 $req_general
变量的 map
块)。
限制效果:
- 对所有 URL 限制请求数,每个 IP 每秒最多请求 20 次,允许 40 个突发请求
- 对 WordPress XML-RPC URL 限制请求数,每个 IP 每秒最多请求 1 次,不允许并发请求
- 对 WordPress 搜索/登录/注册/重置密码 URL 限制请求数,每个 IP 每秒最多请求 5 次,允许 10 个突发请求
http { ... map $uri$is_args$args $limit_req_level { default 0; ~^/xmlrpc\.php 1; ~\?(?:.*&)?s= 2; ~^/wp-login\.php 2; } map $limit_req_level $req_general { default ""; 0 $binary_remote_addr; } map $limit_req_level $req_wpxmlrpc { default ""; 1 $binary_remote_addr; } map $limit_req_level $req_wpsearch_wplogin { default ""; 2 $binary_remote_addr; } limit_req_zone $req_general zone=req_general:10m rate=20r/s; limit_req_zone $req_wpxmlrpc zone=req_wpxmlrpc:10m rate=1r/s; limit_req_zone $req_wpsearch_wplogin zone=req_wpsearch_wplogin:10m rate=5r/s; limit_req_status 429; ... server { ... limit_req zone=req_general burst=40 nodelay; limit_req zone=req_wpxmlrpc nodelay; limit_req zone=req_wpsearch_wplogin burst=10 nodelay; ... } }
limit_req 测试效果
安装 SIEGE WEB 压力测试工具(CentOS 安装命令,需已添加 EPEL 源)。
yum -y install siege
运行命令测试(其中 -r
指定测试次数,-c
指定并发请求数)。
siege -v --no-parser -b -r 1 -c 50 "https://example.com/wp-login.php"
注:当修改 limit_req
配置后,可能 nginx -s reload
不足以刷新配置,需要用 systemctl force-reload nginx
命令刷新。