Nginx 反向代理后端接口,一步步解决跨域冲突、预检报错问题

0 阅读1分钟

前言

日常开发部署后端服务时,常会遇到一个高频难题:前端多域名访问后端接口、Nginx 反向代理 + 后端自带跨域配置,双重冲突导致跨域报错、OPTIONS 预检请求失败、接口请求异常。

前段时间部署项目,后端部署在本地127.0.0.1:8270,前端对接多个业务域名,包含test1.comtest2.com、test3.com等。

初期出现严重跨域问题:

  1. 后端自带跨域响应头,+ Nginx 额外配置跨域,出现重复跨域头
  2. 多域名访问,跨域白域无法统一管理;
  3. 前端 OPTIONS 预检请求无响应,接口直接拦截;
  4. 跨域证书、请求头权限受限,POST、PUT 等请求报错。

本文完整记录,我如何通过 Nginx 配置优化,一站式彻底解决多域名跨域、重复请求头、预检失败全流程问题。

一、问题根源分析

1. 核心矛盾

后端服务本身配置了跨域响应头,前端通过 Nginx 反向代理访问,若 Nginx 再叠加跨域配置,会造成:

  • Access-Control-Allow-Origin 重复报错;
  • 多域名白名单无法统一管控;
  • 浏览器跨域校验规则冲突。

2. 特殊问题

现代前端请求中,POSTPUTDELETE、带Content-TypeAuthorization请求头的接口,会触发 OPTIONS 预检请求

3. 业务需求

项目需要支持多个前端域名访问:

二、解决方案整体思路

采用Nginx 全局接管跨域方案,核心思路四步走:

  1. 屏蔽后端跨域头:禁止后端返回的跨域相关头信息,避免重复冲突;
  2. 配置多域名白名单:正则匹配合法域名,动态配置跨域来源;
  3. 拦截 OPTIONS 预检请求:Nginx 直接返回 204 状态码 + 标准跨域头,不转发后端;
  4. 常规请求统一附加跨域头:所有正常接口请求,统一添加合规跨域配置。

全程不修改后端代码,纯 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";
}

五、配置校验与上线步骤

  1. 校验 Nginx 语法

    nginx -t

若无报错,说明配置语法无误。

  1. 平滑重载生效

    systemctl reload nginx

平滑重启,不中断线上业务。