做前端开发的同学,几乎都踩过跨域的坑; 做后端或运维的同学,大概率也写过 nginx 跨域配置。
但很多人配置时都会有个疑问:
“为什么我加了 access-control-allow-origin,浏览器还是报跨域?” “add_header 和 proxy_set_header 到底哪个才是加跨域头的?”
甚至有人误以为“跨域头要转发给后端”,结果白白走了很多弯路。
今天,我们就从这两个核心指令的区别入手,彻底讲清楚 nginx 如何正确处理跨域,并附上一套开箱即用的配置模板。
一、核心问题:跨域头该用add_header还是proxy_set_header?
这是最容易混淆的点。先说结论:
✅ 处理浏览器跨域,只用 add_header! ❌ proxy_set_header 是用来给后端传请求信息的,和跨域校验本身无关。
很多人踩坑,就是因为把这两个指令的作用对象搞反了。我们用一张表彻底分清:
| 指令 | 作用对象 | 核心目的 | 是否用于跨域校验 |
|---|---|---|---|
| add_header | nginx → 浏览器(响应头) | nginx返回给浏览器的响应添加头信息,相当于nginx帮后端接口补充了header信息 | ✅ 是!跨域头必须用它加 |
| proxy_set_header | nginx → 后端(请求头) | 给转发到后端的请求添加头信息 | ❌ 否!仅用于传递请求上下文(如真实 ip) |
一句话总结:
浏览器跨域校验,只看“最终收到的响应头”里有没有 access-control-\* 字段。 而 add_header 就是专门负责往响应头里加这些字段的; proxy_set_header 加的是请求头,浏览器根本看不到,自然对跨域无效。
二、跨域的本质:是浏览器的安全机制,与后端无关
在讲配置前,先明确一个关键认知:
🔒 跨域是浏览器的行为,不是后端的行为。
举个例子: 前端 http://localhost:8080 请求后端 http://localhost:59200,因端口不同,属于跨域。
这个过程中:
- 后端其实已经正常处理了请求并返回了数据;
- 但浏览器会拦截这份响应,检查其中是否包含允许 http://localhost:8080 访问的声明;
- 如果有,就把数据交给前端;如果没有,就抛出跨域错误。
所以,解决跨域的核心,就是让浏览器收到的响应头中包含合法的 access-control-\* 字段。
而 nginx 的作用,就是在后端未配置这些字段时,替后端“补上”,让浏览器放行。
三、nginx 跨域配置实操:模板 + 逐行解析
下面是一套生产可用的 nginx 跨域配置模板,先看代码,再逐行拆解:
location / {
# 1. 跨域核心响应头(给浏览器看的)
add_header 'access-control-allow-origin' 'http://localhost:8080' always;
add_header 'access-control-allow-methods' 'get, post, put, delete, options' always;
add_header 'access-control-allow-headers' 'content-type, authorization' always;
# 2. 预检请求直接处理,不转发给后端
if ($request_method = 'options') {
return 204;
}
# 3. 反向代理配置(给后端传请求信息)
proxy_pass http://localhost:59200;
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
}
1. 跨域核心:add_header指令详解
这部分是解决跨域的关键,每行都有明确作用,且必须带 always:
- access-control-allow-origin:指定允许访问的前端域名。
- 填具体地址(如 'http://localhost:8080')最安全;
- 若填 '*',则不能携带凭证(如 cookie、authorization),否则浏览器会拒绝。
- access-control-allow-methods:列出前端可能使用的 http 方法(如 get/post/put 等)。
- access-control-allow-headers:声明允许的自定义请求头(如 content-type、authorization)。
- always 关键字: ⚠️ 极其重要! 它确保即使后端返回 404、500 等非 2xx 响应,跨域头依然会被添加。 如果省略 always,错误响应将没有跨域头,浏览器照样拦截——这是最常见的“配了却无效”的原因!
2. 为什么要单独处理options预检请求?
对于 post、put 等“非简单请求”,浏览器会先发一个 options 预检请求,询问服务器:“你允许我这么请求吗?”
如果 nginx 不处理,这个 options 会被转发给后端。而很多后端服务根本没有实现 options 接口,直接返回 404 或 500,导致预检失败。
因此,我们用:
if ($request_method = 'options') {
return 204;
}
让 nginx 直接返回 204(no content),高效通过预检,避免打扰后端。
3.proxy_set_header的真实作用
这部分和跨域无关,纯粹是为了让后端拿到正确的请求上下文:
- proxy_pass:将请求转发到后端服务;
- proxy_set_header host $host:传递原始 host,避免后端路由出错;
- x-real-ip / x-forwarded-for:传递客户端真实 ip,方便日志记录或风控。
💡 再强调一次:这些头是给后端看的,浏览器完全不关心。
4. 完整请求流程图解
结合上述配置,一次跨域请求的完整流程如下:
- 浏览器发送 options 预检请求 → 到达 nginx;
- nginx 匹配 location /,触发 options 处理逻辑,直接返回 204 + 跨域头;
- 浏览器校验通过,发送真实请求(如 post)→ 到达 nginx;
- nginx 通过 proxy_set_header 补充 host/ip 等信息,转发给后端;
- 后端处理并返回业务响应(可能不含跨域头);
- nginx 接收响应,通过 add_header 添加跨域头,再返回给浏览器;
- 浏览器验证响应头合规,将数据交给前端 javascript —— 跨域成功!
四、常见踩坑点 & 避坑指南
❌ 坑 1:加了跨域头还是报错?→ 忘了always
解决方案:所有 add_header 跨域指令必须加上 always。
❌ 坑 2:用proxy_set_header加跨域头
错误示例:proxy_set_header access-control-allow-origin *; 后果:跨域头被加到请求头里,浏览器看不到,完全无效。
❌ 坑 3:allow-origin: *+ 携带authorization
问题:浏览器禁止 * 与凭证(credentials)共存。 正确做法:必须指定具体前端域名,如 'http://localhost:8080'。
❌ 坑 4:跨域头配在server块,不在location块
原因:nginx 中 add_header 不会继承上级块的配置。 正确位置:务必放在实际匹配请求的 location 块内。
五、总结
nginx 处理跨域的核心逻辑其实很简单:
- ✅ 用
add_header给响应头添加跨域字段,让浏览器放行; - ✅ 用
proxy_set_header给请求头补充上下文,让后端正常工作。
记住:
跨域是浏览器的安全策略,后端可以完全不知情。 nginx 充当“中间人”,既帮后端屏蔽了跨域细节,又帮前端拿到了数据。
把上面的配置模板稍作修改(替换你的前端地址和后端地址),就能解决99% 的跨域场景。
到此这篇关于nginx 跨域配置中add_header 和 proxy_set_header区别的文章就介绍到这了,更多相关nginx add_header 和 proxy_set_header内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论