nginx通过proxy_intercept_errors+error_page、map指令或lua脚本实现后端状态码替换,核心是拦截原始错误码并返回自定义状态与响应体;三者分别适用于固定规则、多条件映射和上下文动态处理场景。

Nginx 本身不直接“修改”后端返回的状态码,而是通过拦截 + 替换机制,在代理层统一控制对外响应的状态。核心思路是:不让原始错误码透传,转而用自定义逻辑生成新状态码和响应体。
用 proxy_intercept_errors + error_page 拦截并替换状态码
这是最常用、最稳妥的原生方案,适合规则固定、无需上下文判断的场景。
必须同时启用
proxy_intercept_errors on;和对应error_page,缺一不可error_page后加等号(如=200)表示替换原始状态码;不加等号则只是跳转,需在目标 location 中显式return-
示例配置:
location /api/ { proxy_pass https://backend; proxy_intercept_errors on; # 把后端 404 统一转成 200,并返回空 JSON error_page 404 =200 /empty.json; # 把后端 502/503/504 全部转为 599(网关内部错误) error_page 502 503 504 =599 /err.json; } location = /empty.json { internal; add_header Content-Type application/json; return 200 '{"data":null,"code":0}'; } location = /err.json { internal; add_header Content-Type application/json; return 599 '{"code":599,"msg":"Service unavailable"}'; }
用 map 指令实现多条件映射
当需要根据请求路径、Header 或上游响应头动态决定状态码时,map 更清晰可控,避免 if 嵌套风险。
map可基于$upstream_http_*变量读取后端响应头(如X-App-Code)配合
error_page实现语义转换,不依赖 Lua-
示例:将后端返回的
X-App-Code: 1001映射为 HTTP 400map $upstream_http_x_app_code $mapped_status { 1001 400; 1002 401; 2001 500; default 200; } server { location /api/ { proxy_pass https://backend; proxy_intercept_errors on; error_page 400 = @map_error; error_page 401 = @map_error; error_page 500 = @map_error; } location @map_error { internal; return $mapped_status '{"code":$mapped_status,"msg":"Mapped error"}'; } }
用 Lua 脚本做上下文感知的动态处理
适用于规则复杂、需调用外部配置中心或解析响应体内容的场景(如 JSON 中的 code 字段)。
在
body_filter_by_lua_block中可读取响应体,在header_filter_by_lua_block中可改状态码必须关闭缓冲:
proxy_buffering off;,否则 Lua 无法流式获取完整 body-
示例(简化版):
location /api/ { proxy_pass https://backend; proxy_buffering off; proxy_set_header Connection ''; chunked_transfer_encoding off; header_filter_by_lua_block { local code = tonumber(ngx.var.upstream_http_x_app_code) if code == 1001 then ngx.status = 400 elseif code == 2001 then ngx.status = 503 end } }
注意几个关键细节
-
proxy_intercept_errors只对状态码 ≥400 且响应体非空的响应生效;若后端返回无 body 的 404,可能不触发拦截 -
add_header不能改变状态码,只能加响应头;真正改状态必须用return、error_page =xxx或ngx.status - 状态码处理与重试(
proxy_next_upstream)无关,二者逻辑独立 - 调试时用
curl -v查看真实响应头和 body,确认后端是否返回了标准格式
不复杂但容易忽略











