前言
日常开发部署后端服务时,常会遇到一个高频难题:前端多域名访问后端接口、Nginx 反向代理 + 后端自带跨域配置,双重冲突导致跨域报错、OPTIONS 预检请求失败、接口请求异常。
前段时间部署项目,后端部署在本地127.0.0.1:8270,前端对接多个业务域名,包含test1.com、test2.com、test3.com等。
初期出现严重跨域问题:
- 后端自带跨域响应头,+ Nginx 额外配置跨域,出现重复跨域头;
- 多域名访问,跨域白域无法统一管理;
- 前端 OPTIONS 预检请求无响应,接口直接拦截;
- 跨域证书、请求头权限受限,POST、PUT 等请求报错。
本文完整记录,我如何通过 Nginx 配置优化,一站式彻底解决多域名跨域、重复请求头、预检失败全流程问题。
一、问题根源分析
1. 核心矛盾
后端服务本身配置了跨域响应头,前端通过 Nginx 反向代理访问,若 Nginx 再叠加跨域配置,会造成:
Access-Control-Allow-Origin重复报错;- 多域名白名单无法统一管控;
- 浏览器跨域校验规则冲突。
2. 特殊问题
现代前端请求中,POST、PUT、DELETE、带Content-Type、Authorization请求头的接口,会触发 OPTIONS 预检请求。
3. 业务需求
项目需要支持多个前端域名访问:
二、解决方案整体思路
采用Nginx 全局接管跨域方案,核心思路四步走:
- 屏蔽后端跨域头:禁止后端返回的跨域相关头信息,避免重复冲突;
- 配置多域名白名单:正则匹配合法域名,动态配置跨域来源;
- 拦截 OPTIONS 预检请求:Nginx 直接返回 204 状态码 + 标准跨域头,不转发后端;
- 常规请求统一附加跨域头:所有正常接口请求,统一添加合规跨域配置。
全程不修改后端代码,纯 Nginx 配置优化,零业务改动、低风险、一键生效。
三、分步落地配置优化
第一步:屏蔽后端原生跨域响应头
问题核心源头就是「后端 + Nginx 双重跨域」,优先禁用后端所有跨域相关头,杜绝冲突。
使用proxy_hide_header指令,屏蔽后端返回的跨域关键头:
nginx
# 1. 先统一隐藏后端所有跨域头(解决重复冲突问题)
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;
配置作用:后端无论如何配置跨域,都不会传递到前端,所有跨域逻辑全权交给 Nginx 控制。
第二步:配置多域名跨域白名单
采用$http_origin获取访问来源,通过正则匹配合法域名,动态赋值跨域来源,支持主域名 + www 子域名。
覆盖当前所有业务域名:
nginx
# 2. 配置多域名白名单,动态匹配合法来源
set $cors_origin "";
if ($http_origin ~* "^https://(www\.)?test1\.com$") {
set $cors_origin $http_origin;
}
if ($http_origin ~* "^https://(www\.)?test2\.com$") {
set $cors_origin $http_origin;
}
if ($http_origin ~* "^https://(www\.)?test3\.com$") {
set $cors_origin $http_origin;
}
- 正则
(www\.)?:兼容有无 www 前缀; - 仅匹配合法域名,非法来源不赋值,杜绝跨域漏洞;
- 动态赋值,符合浏览器跨域同源策略要求。
第三步:特殊处理 OPTIONS 预检请求
前端复杂请求必先发送 OPTIONS 预检,后端无对应处理逻辑,极易报错。
解决方案:Nginx 直接拦截 OPTIONS 请求,返回 204 无内容状态码,并附加完整跨域头,无需转发后端:
nginx
# 3. 拦截OPTIONS预检请求,直接返回204状态码
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET,POST,OPTIONS,PUT,DELETE" always;
add_header Access-Control-Allow-Headers "Content-Type,Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
return 204;
}
- 配置全量请求方式:GET/POST/PUT/DELETE/OPTIONS;
- 放行常用请求头:格式头、授权头;
- 开启跨域凭证支持,适配登录鉴权场景。
第四步:常规接口统一附加跨域头
除预检请求外,所有 GET、POST 等正常业务请求,统一添加跨域响应头,保证接口正常访问:
nginx
# 4. 正常业务请求,统一添加跨域配置
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET,POST,OPTIONS,PUT,DELETE" always;
add_header Access-Control-Allow-Headers "Content-Type,Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
always关键字至关重要:无论响应状态码 200/400/500,都强制携带跨域头,避免异常接口跨域失效。
第五步:保留原有反向代理核心配置
跨域配置完全独立隔离,原有反向代理、IP 转发、超时、WebSocket 升级等配置完全保留,不影响原有业务运行:
nginx
# 原有反向代理配置,完全保留无修改
proxy_pass http://127.0.0.1:8270;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
proxy_set_header X-Host $host:$server_port;
proxy_set_header X-Scheme $scheme;
proxy_connect_timeout 30s;
proxy_read_timeout 86400s;
proxy_send_timeout 30s;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
四、完整最终 Nginx 配置
location / {
# 1. 先统一隐藏后端所有跨域头(解决重复冲突问题)
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;
# 2. 配置多域名白名单,动态匹配合法来源
set $cors_origin "";
if ($http_origin ~* "^https://(www\.)?test1\.com$") {
set $cors_origin $http_origin;
}
if ($http_origin ~* "^https://(www\.)?test2\.com$") {
set $cors_origin $http_origin;
}
if ($http_origin ~* "^https://(www\.)?test3\.com$") {
set $cors_origin $http_origin;
}
# 3. 拦截OPTIONS预检请求,直接返回204状态码
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET,POST,OPTIONS,PUT,DELETE" always;
add_header Access-Control-Allow-Headers "Content-Type,Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
return 204;
}
# 4. 正常业务请求,统一添加跨域配置
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET,POST,OPTIONS,PUT,DELETE" always;
add_header Access-Control-Allow-Headers "Content-Type,Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
# 原有反向代理核心配置,无任何修改
proxy_pass http://127.0.0.1:8270;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
proxy_set_header X-Host $host:$server_port;
proxy_set_header X-Scheme $scheme;
proxy_connect_timeout 30s;
proxy_read_timeout 86400s;
proxy_send_timeout 30s;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
五、配置校验与上线步骤
-
校验 Nginx 语法
nginx -t
若无报错,说明配置语法无误。
-
平滑重载生效
systemctl reload nginx
平滑重启,不中断线上业务。