Nginx中如何设置limit_req限制内部重定向,避免递归重定向引发雪崩?
- 内容介绍
- 文章标签
- 相关推荐
本文共计719个文字,预计阅读时间需要3分钟。
请提供需要改写的伪原创开头内容,我将根据您的要求进行修改。
识别重定向行为的关键变量
内部重定向不会改变客户端 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,让失败重试不加剧循环
本文共计719个文字,预计阅读时间需要3分钟。
请提供需要改写的伪原创开头内容,我将根据您的要求进行修改。
识别重定向行为的关键变量
内部重定向不会改变客户端 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,让失败重试不加剧循环

