Nginx 无内置重定向次数限制,但可通过 limit_req 模块配合 map 定义重定向深度变量、对高危 location(如 named location 或含 rewrite/return 的路径)施加严格限流,并结合 Lua 计数与详细日志定位源头,实现有效防护。

nginx 本身不直接提供“内部重定向次数”的硬性限制指令,但通过 limit_req 模块配合合理变量设计,可以有效遏制因配置错误、逻辑缺陷或恶意构造导致的内部重定向循环(如 try_files + rewrite 无限跳转、Lua 中误触发重定向等),从而防止后端雪崩。
识别重定向行为的关键变量
内部重定向不会改变客户端 IP 或原始请求 URI,但会更新 $request_uri 和 $uri。更可靠的方式是利用 nginx 内置变量标记“重定向深度”:
-
$request_length:虽不直接反映次数,但异常长的请求头(如反复添加自定义 header)可作为辅助线索 -
$sent_http_location:仅在返回 301/302 时存在,无法用于拦截内部重定向(它不触发响应头发送) -
推荐做法:用
map构造自定义 key,例如:
map $request_uri $redirect_depth {
~^/api/.*\?redirect=1$ 1;
~^/api/.*\?redirect=2$ 2;
default 0;
}
再结合limit_req_zone $redirect_depth zone=redir_check:1m rate=1r/s,对深度 ≥2 的请求强制限速
用 limit_req 拦截高频内部跳转
真正起作用的是把“可能引发递归的路径”单独圈出,并施加极严限流。例如:
- 所有含
rewrite ... last或return 301的 location,统一加limit_req zone=redir_protect burst=1 nodelay - 对
try_files @fallback后的 named location(如@fallback),显式配置限流:
location @fallback {
limit_req zone=redir_protect burst=1 nodelay;
proxy_pass http://backend;
} - 若使用 OpenResty/Lua,可在
access_by_lua*中设置ngx.var.redir_count = tonumber(ngx.var.redir_count or "0") + 1,再用map $redir_count $is_deep_redir { ~^[2-9]\d*$ 1; default 0; }触发限流
配合日志快速定位递归源头
没有日志,就无法判断是哪个环节在反复跳转。必须开启详细记录:
- 在
http块定义日志格式,包含关键变量:
log_format redir_log '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time $limit_key $limit;' - 在对应 location 中启用该格式:
access_log /var/log/nginx/redir.log redir_log; - 重点关注
$limit_key值是否重复出现同一组合(如固定$binary_remote_addr+$uri循环),以及$request_time是否持续增长
避免误伤与防御分层
单纯靠限流不能替代逻辑修复,需同步做减法:
- 禁用不必要的
rewrite ... redirect,优先用return 301显式控制 - 对
try_files链条做长度限制,例如最多允许两级 fallback:
try_files $uri $uri/ @level1;
location @level1 { try_files $uri @level2; }
location @level2 { limit_req zone=redir_protect burst=1 nodelay; proxy_pass ...; } - 在 upstream 中配置
max_fails=1 fail_timeout=1s,让失败重试不加剧循环











