必须加 always 参数并确保 proxy_pass 所在 location 块中统一配置,同时为 error_page 对应的 location 显式添加 always,才能使 500 等错误响应携带跨域头。

要让 Nginx 在代理到微服务时,即使后端返回 500 错误,也能保留跨域响应头(如 Access-Control-Allow-Origin),关键在于两点:强制头生效 + 覆盖错误响应路径。
必须加 always 参数
Nginx 默认只对 2xx 和 3xx 响应添加 add_header 设置的头,4xx/5xx 错误响应(包括 500)会被跳过。不加 always,前端收到 500 时就看不到跨域头,浏览器直接拦截响应体,无法读取错误详情。
-
✅ 正确写法(所有状态码都生效):
add_header Access-Control-Allow-Origin "https://your-frontend.com" always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" always; add_header Access-Control-Allow-Credentials "true" always;
-
❌ 错误写法(500 时不生效):
add_header Access-Control-Allow-Origin "https://your-frontend.com"; # 缺少 always
确保 proxy_pass 所在 location 块中统一配置
CORS 头必须放在实际代理请求的 location 块里(例如 location /api/),而不是 server 或根 location /。否则:
- 头可能被应用层响应覆盖;
- 错误响应(如后端崩了返回 500)仍走该 location,但若没加
always,头就丢了。
特别注意自定义错误页场景
如果你启用了 error_page 500 /50x.html;,Nginx 会把 500 响应重定向到静态页面,此时请求进入 location = /50x.html 分支——这个分支若没配 add_header ... always,跨域头就彻底丢失。
正确做法是显式为错误页 location 补充头:
error_page 500 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
add_header Access-Control-Allow-Origin "https://your-frontend.com" always;
add_header Access-Control-Allow-Methods "GET" always;
}验证是否生效
用 curl 触发一个必然失败的接口(比如故意调用不存在的 endpoint 或触发后端异常):
curl -I -H "Origin: https://your-frontend.com" https://your-domain.com/api/bad-path
检查响应头里是否有 Access-Control-Allow-Origin。有,说明 always 生效;没有,说明位置或语法有误。
不复杂但容易忽略


















