必须由nginx统一控制跨域响应头,后端禁用cors输出;先用proxy_hide_header逐条屏蔽后端返回的access-control-allow-origin等头,再用add_header always统一注入合规头,并通过if判断单独处理options请求返回204。

必须由 Nginx 统一控制跨域响应头,后端服务不能自行输出 Access-Control-Allow-Origin 等 CORS 头。一旦后端和 Nginx 同时设置,浏览器会收到重复头,直接拒绝响应——即使状态码是 200,控制台也会报 “has been blocked by CORS policy” 或 “contains multiple values”。
先屏蔽后端返回的 CORS 头
在 location 块中,用 proxy_hide_header 明确拦截后端透传的冲突头:
proxy_hide_header Access-Control-Allow-Origin;proxy_hide_header Access-Control-Allow-Methods;proxy_hide_header Access-Control-Allow-Headers;proxy_hide_header Access-Control-Allow-Credentials;proxy_hide_header Access-Control-Expose-Headers;
注意:该指令只对代理响应生效,且需写在 proxy_pass 所在的 location 内;它不支持正则,必须逐条列出要隐藏的头名。
再由 Nginx 统一注入合规头
使用 add_header ... always 确保所有成功响应(包括 204)都携带头:
add_header 'Access-Control-Allow-Origin' 'https://your-frontend.com' always;add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;add_header 'Access-Control-Allow-Credentials' 'true' always;
若需支持多个前端域名,改用 map 指令做白名单匹配,避免硬编码或通配符引发凭证冲突。
单独拦截并响应 OPTIONS 预检请求
浏览器对非简单请求(如带 Authorization、PUT、application/json)必发 OPTIONS 请求。Nginx 必须自己处理,不能转发给后端:
- 用
if ($request_method = 'OPTIONS') { ... return 204; }截断流程 - 在
if块内补全预检所需头:Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Max-Age - 不要在预检响应里加
Access-Control-Allow-Credentials—— 它仅用于实际请求
验证与排查要点
配置生效后,用 curl -I -X OPTIONS https://your-domain/api/xxx 检查响应头:
- 确认响应中没有重复的
Access-Control-Allow-Origin - 确认 OPTIONS 请求返回 204,且含完整 CORS 头
- 确认后端服务已关闭所有 CORS 中间件(如 Spring 的
@CrossOrigin、Express 的cors()) - 重启 Nginx 并检查错误日志:
nginx -t && systemctl reload nginx











