Nginx 不跳转跨域,而是通过 rewrite 重写路径 + proxy_pass 反向代理 + add_header 设置 CORS 响应头,使浏览器允许前端同源请求访问跨域后端接口。

直接说结论:Nginx 本身不“跳转跨域”,而是通过 rewrite 改写请求路径 + add_header 开启 CORS 响应头,让浏览器允许前端跨域访问后端接口。所谓“跨域跳转”其实是前端发请求到 Nginx(同源),Nginx 内部反向代理到真实后端(可能跨域服务),同时注入合法 CORS 头,从而绕过浏览器同源策略限制。
一、明确场景:为什么不用代理转发而要 rewrite?
常见误区是把 rewrite 当作“跳转到另一个域名”。其实 rewrite 是 URL 重写(服务端内部路径变更),不是 HTTP 301/302 跳转。真正实现“跨域可访问”的关键是:让前端请求打到 Nginx(同源地址),Nginx 改写路径后 proxy_pass 到目标服务,并透传或设置合适的 Access-Control-* 头。
典型适用场景:
- 前端部署在
https://app.example.com,调用接口需走/api/v1/users - 后端服务实际运行在
https://backend.internal:8080(内网)或https://api.prod.com(外网) - 浏览器禁止前端直连
api.prod.com,但允许访问同域的app.example.com/api/...
二、rewrite + proxy_pass + CORS 的最小可行配置
以下是一个生产可用的 Nginx server 块示例(假设监听 443,域名 app.example.com):
// 注意:rewrite 在 location 内部执行,优先于 proxy_pass;proxy_set_header 保证后端收到原始 Host 和协议信息
server {
listen 443 ssl;
server_name app.example.com;
<pre class="brush:php;toolbar:false;"># SSL 配置略(cert/key)
# 匹配所有 /api/ 开头的请求
location ^~ /api/ {
# 重写:去掉前缀 /api,转发到后端根路径
rewrite ^/api/(.*)$ /$1 break;
# 代理到真实后端(可内网 IP 或域名)
proxy_pass https://backend.internal:8080;
# 必须透传关键头,否则后端拿不到原始信息
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 X-Forwarded-Proto $scheme;
# ✅ 关键:启用 CORS 响应头(允许任意源,生产环境请按需收紧)
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
# 处理预检请求(OPTIONS)
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Max-Age' 1728000 always;
add_header 'Content-Type' 'text/plain; charset=utf-8' always;
add_header 'Content-Length' 0 always;
return 204;
}
}
# 其他静态资源或首页
location / {
root /var/www/app;
try_files $uri $uri/ /index.html;
}
}
三、rewrite 的常见写法与避坑点
rewrite 规则不是万能跳转器,要注意匹配顺序、flag 含义和上下文:
- ^~ /api/:前缀匹配且优先级高于正则,适合 API 路由,避免被后续正则 location 拦截
- rewrite ^/api/(.*)$ /$1 break;:break 表示重写后不再匹配新 location,直接用当前 location 的 proxy_pass;用 last 会重新匹配 location,易引发循环
- 如果后端要求保留 /api 前缀(如 Spring Boot 的 context-path),就不要 rewrite,直接 proxy_pass https://backend/internal/api/
- rewrite 不处理 query string(?a=1&b=2),它自动透传;如需修改参数,要用 $args 变量或 set 指令配合 map
四、CORS 头必须加在响应中,且不能被缓存覆盖
很多问题出在 header 没生效:
- 用
add_header ... always,否则 301/302/4xx 等非 200 响应不会带 CORS 头 - 避免在 upstream 或 http 块全局 add_header,容易被子块覆盖;建议在具体 location 内添加
- 如果用了 CDN 或 WAF,确认它们没有过滤或覆盖 Access-Control-* 头
- 前端 fetch 调用时若带 credentials(如 cookies),
Access-Control-Allow-Origin不能为 *,需指定确切域名,并加上Access-Control-Allow-Credentials true
不复杂但容易忽略:rewrite 是路径改写,CORS 是响应授权,两者配合才能让跨域请求“看起来像同源”。核心不在跳,而在“代劳+放行”。










