recursive_error_pages on 仅开启多层 error_page 跳转,真正实现降级需 error_page + 命名 location + proxy_intercept_errors/fastcgi_intercept_errors 组合,且必须同作用域、逐层拦截、链路闭环终止。

recursive_error_pages 本身不定义降级路径,它只是允许 error_page 在一次请求中多次触发——换句话说,是“开启多层跳转的开关”。真正构建多级降级逻辑的,是 error_page + 命名 location + proxy_intercept_errors / fastcgi_intercept_errors 的组合。配置关键不在“加 on”,而在“链路闭环”。
必须放在同级或上级作用域,且与 error_page 共存
recursive_error_pages on 必须和它要服务的 error_page 指令处于同一 server 或 location 块内(不能只写在 http 块里指望子块自动生效)。
例如:
server {
listen 80;
server_name example.com;
recursive_error_pages on; # ✅ 放在 server 块顶层,覆盖所有内部 location
location /api/ {
proxy_pass http://main_api;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback_v1;
}
location @fallback_v1 {
proxy_pass http://backup_api;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback_v2; # ✅ 同一 location 内,可再跳
}
location @fallback_v2 {
internal;
root /var/www/degrade;
try_files /maint.json =503; # ✅ 终止链路,不再跳
}
}- ❌ 错误:
recursive_error_pages on只写在http{}里,而error_page在某个location中 → 不生效 - ✅ 正确:两者共处
server或嵌套的location,确保作用域一致
每一级都要显式拦截并绑定错误码
Nginx 默认不拦截后端错误(如 PHP 抛异常、代理返回 502),必须逐层启用拦截,并明确列出要捕获的状态码:
- 对于 FastCGI(PHP):用
fastcgi_intercept_errors on; - 对于反向代理(upstream):用
proxy_intercept_errors on; - 两者不可互换,且必须和
error_page在同一个 location 块内
常见遗漏:
- 忘了在
@fallback_v1里加proxy_intercept_errors on→ 第二次 502 不会触发@fallback_v2 - 在 PHP location 里只配了
proxy_intercept_errors(无效)→ 应该用fastcgi_intercept_errors
链路必须有明确终点,避免循环或超限
Nginx 递归上限为 10 层。只要任意一环没终止,第 11 次重定向就会报错:rewrite or internal redirection cycle while internally redirecting to ...
安全终止方式包括:
- 使用
try_files返回静态文件(如/degrade/503.json),后面接=404或=503 - 用
return直接响应,不依赖外部服务:location @final_fallback { internal; return 200 '{"status":"degraded","data":[]}'; add_header Content-Type application/json; } - 加入条件判断(如用
if ($request_uri !~ ^/degrade) { ... }),防止重复进入同一命名 location
结合 fastcgi_intercept_errors 处理 PHP 错误需额外注意
如果降级目标是 PHP 脚本出错(比如 throw new Exception()),仅靠 recursive_error_pages 和 proxy_* 指令完全无效:
- PHP 错误由 FastCGI 直接返回 HTTP 500 给客户端,Nginx 根本收不到
- 必须在 PHP location 中启用:
fastcgi_intercept_errors on; error_page 500 = @php_degrade;
- 并确保 PHP-FPM 实际返回的是 500 状态码(而非 200 + HTML 错误页),可通过
curl -I验证响应头
不复杂但容易忽略

















