Nginx 的 add_header 默认不为 5xx 响应添加 CORS 头,导致浏览器误报跨域错误而非真实服务故障;启用 add_header ... always 可强制所有状态码返回 CORS 头,需配合 OPTIONS 处理和 error_page 配置确保全覆盖。

在 Nginx 中,add_header 默认只在响应状态码为 200、201、204、206、301、302、303、304、307 等“成功”或“重定向”类状态码时生效;遇到 5xx 错误(如 500、502、503、504)时,这些头部默认被跳过——这会导致前端在服务异常时收不到 Access-Control-Allow-Origin 等关键 CORS 头,从而触发浏览器的跨域拦截,掩盖真实错误,甚至让调试变得困难。
为什么 5xx 响应不带 CORS 头很危险
当后端崩溃返回 502 Bad Gateway,但 Nginx 没有透传 CORS 头时,浏览器会因缺失 Access-Control-Allow-Origin 直接报 CORS 错误,而不是显示后端真实的 502。开发者看到的只是“跨域被拒绝”,误以为是配置问题,实际是服务已不可用。
- 前端无法区分“跨域被拒”和“服务宕机”,错误归因失真
- 监控告警可能漏掉真实故障(只捕获了前端报错,而非后端状态码)
- 预检请求(OPTIONS)若也失败,整个跨域流程彻底中断
add_header always 的作用与写法
always 是 add_header 的可选修饰符,自 Nginx 1.7.5 起支持。加上它后,对应响应头会在所有状态码下强制添加,包括 4xx 和 5xx。
正确写法示例:
location /api/ {
proxy_pass http://backend;
add_header 'Access-Control-Allow-Origin' 'https://myapp.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
}
注意:always 必须紧贴在值之后、分号之前,不能换行或加空格;且仅对当前 location 或 server 块内生效。
配合预检请求与错误页的完整防护
仅加 always 不够,还需确保 OPTIONS 预检本身也能返回正确 CORS 头,尤其在出错时:
- 显式处理 OPTIONS 请求:在 location 中添加
if ($request_method = 'OPTIONS') { add_header ... always; return 204; } - 避免 error_page 覆盖:如果用了
error_page 502 /50x.html,要确保该自定义错误页所在 location 也配置了带always的 CORS 头,否则 502 响应体换成 HTML 后仍无 CORS 头 - 慎用通配符:生产环境避免
Access-Control-Allow-Origin: *与Access-Control-Allow-Credentials: true共存,否则浏览器会直接拒绝
验证是否生效
用 curl 模拟 5xx 响应并检查头部:
curl -I -H "Origin: https://myapp.com" http://localhost/api/fail
观察输出中是否包含 Access-Control-Allow-Origin;也可在浏览器 DevTools 的 Network 面板中触发一个必然失败的接口调用,查看 500/502 响应的 Response Headers 是否完整。
安全和可用性兼顾的关键就在这里:CORS 头不该只在“一切正常”时才存在,而应在每一次响应中可靠出现——包括系统出错的时候。











