马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
前后端分离之后,跨域几乎是必过的一关。浏览器报的那串 "has been blocked by CORS policy" 往往让新手以为后端挂了,其实数据 200 已经回来了,是浏览器拦下了响应。这篇用一台装了 nginx 的机器现场复现跨域报错,再给出 add_header 的完整解法,顺带把同源策略、Access-Control-Allow-Origin、Access-Control-Allow-Methods 三个概念一次讲透。
一、成因与目标
1. 为什么要跨域
同源策略限制:协议、域名、端口号三者全同才算同源。当 Web 应用要和不同源的服务器交换数据、或嵌入异域的视频图片时,就涉及跨域。
2. 跨域的三个真实用途
- 数据共享与交互:电商网站要拿供应商服务器的实时库存,两边域名天生不同。
- 多平台协作:不同团队/公司维护不同域名与部署环境,第三方应用要把分享请求传给对方平台。
- 资源复用:字体库、JS 库、图片库放在专门的 CDN 域名上,网站引用它们同样是跨域。
二、什么是同源
两个 URL 的协议、域名、端口号相同即为同源。
同源示例:
http://www.example.com/index.html
http://www.example.com/page.html
两处都是 HTTP、域名 www.example.com、端口用 HTTP 默认 80(URL 里没写就默认 80)。
不同源示例:
https://www.example.com/index.html # 协议是 HTTPS
http://api.example.com/index.html # 域名不同
http://www.example.com:8080/... # 端口 8080 ≠ 80
同源策略是浏览器安全的基石:它限制不同源文档或脚本之间的交互,防止恶意网站通过脚本摸到别的站点的敏感信息。
三、实验准备
一台装好 nginx 的机器,准备两件事:
/opt/nginx/a.html:页面里放一个按钮,点击后 $.get('http://192.168.72.130:8888/getUser');
- 两个监听不同端口的 server,一个 9999 出页面,一个 8888 出数据。
1. 页面文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>跨域问题演示</title>
<script src="jquery.js"></script>
</head>
<body>
<button id="btn">请求数据</button>
<script>
$(function(){
$("#btn").click(function(){
$.get('http://192.168.72.130:8888/getUser', function(data){
alert(JSON.stringify(data));
});
});
});
</script>
</body>
</html>
坑点先讲:jquery.js 必须提前放在和 a.html 同一个目录,否则控制台直接抛 Uncaught ReferenceError: $ is not defined。这个是引用路径问题,和跨域无关,却经常掩盖真正的跨域报错。
2. 两个不同源的 server
server {
listen 9999;
server_name localhost;
gzip on;
root /opt/nginx;
location / {
index a.html;
}
}
server {
listen 8888;
server_name localhost;
gzip on;
root /opt/nginx;
location /getUser {
default_type application/json;
return 200 '{"id":"123","name":"小明","age":"18"}';
}
}
访问 http://192.168.72.130:8888/getUser 能拿到 JSON,访问 http://192.168.72.130:9999/ 能出页面,但页面按钮点了没反应——跨域就在这一步发生了。
四、报错现象分析
浏览器控制台会给出这段:
Access to XMLHttpRequest at 'http://192.168.72.130:8888/getUser'
from origin 'http://192.168.72.130:9999' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
拆成两条逻辑:
- 同源策略与 CORS:请求源是 9999,目标资源源是 8888,端口不同就是跨域,浏览器按同源策略拦。
- Access-Control-Allow-Origin 头缺失:浏览器检查响应头里有没有这个字段,没有就拒绝把响应交给脚本——哪怕服务端已经 200 返回了数据。
所以第二行 GET net::ERR_FAILED 200 (OK) 看着矛盾:200 是服务器说"我处理完了",ERR_FAILED 是浏览器说"我不给你用"。这也解释了为什么很多人 curl 接口明明通、页面却取不到数据。
五、解决
1. 方案说明
用 add_header 指令补响应头。它的语法与默认值:
| 指令 | 语法 | 默认值 | 可放位置 |
| add_header | add_header name value ... | — | http、server、location |
这里要补两个头:
- Access-Control-Allow-Origin:允许跨域访问的源地址,可配多个(逗号分隔),也可用
* 代表所有源。
- Access-Control-Allow-Methods:允许的请求方式,GET/POST/PUT/DELETE 等,多个逗号分隔,按需开放不要图省事全给。
server {
listen 8888;
server_name localhost;
gzip on;
root /opt/nginx;
location /getUser {
# 允许 http://192.168.72.130:9999 这个源访问
add_header Access-Control-Allow-Origin http://192.168.72.130:9999;
# 允许的请求方法
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
default_type application/json;
return 200 '{"id":"123","name":"小明","age":"18"}';
}
}
把 Origin 写死成具体源,比 * 安全得多:生产环境用 * 意味着任何站点都能读你的接口,配合 Cookie 时甚至会直接违反浏览器规范。
3. 验证
sudo nginx -t
sudo systemctl reload nginx
改完先做语法检查再 reload,别一上来就 restart。回车后回到 http://192.168.72.130:9999/ 再点按钮,弹出 JSON 即跨域打通。
六、三个补充场景
- 自定义请求头或 application/json:会触发预检 OPTIONS,需要在 location 里补
if ($request_method = OPTIONS) { return 204; } 并回 Access-Control-Allow-Headers。
- 带 Cookie:必须把 Access-Control-Allow-Credentials 设为 true,同时 Origin 不能是 *,前端请求要带 credentials。
- 纯静态资源跨域引用:img、script、link 这类标签不受 CORS 限制,只有 XMLHttpRequest / fetch 才受同源策略管。
跨域问题的本质只有一句:服务端给不给"你准予访问"这个头。明白了这点,报错信息就不再神秘——看它指哪个头缺失,补上就好。
qidao123.com企服评测-应用市场 编辑提示
本文按同源的定义与四个非同源示例、CORS 的三种业务用途、双 server 不同端口复现环境、a.html 与 jquery.js 同目录的引用要点、Access to XMLHttpRequest 报错的逐段拆解、add_header 指令的语法默认值与 Access-Control-Allow-Origin / Methods 两个头、以及预检 OPTIONS 与带 Cookie 场景的差异,重排成"现象—原因—解决—扩展"的闭环。文中 IP、端口、路径与示例 JSON 均为实验示意值,配置项取自 nginx 官方文档,请以实际环境为准;跨域头在生产环境建议按具体源白名单配置并在自有服务上验证。 |