recursive_error_pages 不支持循环重定向,仅允许 error_page 内部错误触发最多10层嵌套处理;超限返回500并记录日志,与http重定向无关。

recursive_error_pages 本身不处理 HTTP 重定向(如 302),也不用于解决“多级重定向导致的死循环”——它只控制 error_page 触发后的**内部错误嵌套处理层级**,最多 10 层。所谓“死循环”,往往是配置不当引发的 rewrite 或 error_page 链路无终点所致,而非真正的循环重定向需求。
明确作用边界:它不是为 redirect 设计的
recursive_error_pages on 仅在以下场景生效:
- 某个 location 返回了错误码(如 502、404)→ 触发
error_page 502 = @fallback - @fallback 这个 named_location 内部又出错(如返回 500、或 try_files 全部失败触发 404)→ 若开启 recursive_error_pages,Nginx 才会再次查找匹配的 error_page
- 这个过程最多重复 10 次,超限即报
rewrite or internal redirection cycle并返回 500
它和 return 302、rewrite ... redirect、浏览器跳转完全无关。那些属于 HTTP 协议层行为,由客户端执行,Nginx 不递归拦截也不受该指令影响。
常见“伪死循环”来源与解法
你以为是 recursive_error_pages 导致循环,实际多是以下两类配置错误:
-
error_page 无条件指回自身:
例如:location @fallback { proxy_pass http://backup; error_page 502 = @fallback; }→ 每次失败都重进同一 location,必然超 10 层 -
rewrite 规则缺少终止条件:
比如用rewrite ^/api/(.*)$ /v2/$1 break;后没配好 v2 的处理逻辑,又触发另一个 rewrite,形成内部重写链断裂
✅ 正确做法是强制链路有终点:
- 在 fallback location 中用
try_files /static/fallback.json =404;提供静态兜底 - 或直接返回内容:
return 200 '{"status":"fallback"}';,彻底避免二次出错 - 若需动态判断,可用
if ($request_uri ~* "^/api/v2/") { ... }加条件分流,不盲目 fallback
需要真正多跳时,换用 rewrite + break/last
如果目标是按规则多次内部重写(比如兼容旧路径、做 API 版本路由),应放弃依赖 error_page 递归,改用:
-
rewrite ^/old/(.*)$ /new/$1 break;→ 重写后不再重新匹配 location,安全可控 -
rewrite ^/v1/(.*)$ /v2/$1 last;→ 重写后重新走 location 匹配,适合需多级适配的场景,但必须配合location /v2/ { ... }明确定义终点
⚠️ 注意:last 可能再次触发 rewrite,务必用 if、map 或 location 前缀确保最终落入一个不 rewrite 的终点块。
调试与验证关键点
配置后是否生效,不能只看页面,要查日志和状态码:
- 打开 Nginx 错误日志:
error_log /path/to/error.log notice;,出现rewrite or internal redirection cycle就说明链路失控 - 确认
recursive_error_pages on和对应error_page在同一作用域(如都在 server 块内),且不是仅写在 http 块却指望子 location 继承 - phpEnv 用户特别注意:改完配置必须执行
nginx -s reload或在控制台选「重载 Nginx 配置」,点「重启服务」可能只重启 PHP-FPM - PHP 抛异常默认不触发 error_page —— 要让 Nginx 拦截 500,必须加
fastcgi_intercept_errors on;并确保 PHP 返回的是 HTTP 500 响应头,而非 HTML 错误页










