跨域(CORS)原因及解决方案
文章目录
什么是跨域?
浏览器的同源策略
浏览器有一个重要的安全机制,叫 同源策略(Same-Origin Policy)。它的意思是:浏览器只允许网页向和自己“同源”的地址发送请求,否则就拦截。
同源的定义是:
协议(http/ https)、域名(或 IP)、端口号 三者完全相同。
假设你的网页地址是:
http://localhost:3000/index.html
目标地址:
| 目标地址 | 是否同源 | 原因 |
|---|---|---|
| http://localhost:3000/api/data | ✅同源 | 协议、域名、端口号都一样 |
| https://localhost:3000/api/data | ❌不同源 | 协议不同(https 🆚 http) |
| http://127.0.0.1:3000/api/data | ❌不同源 | 域名不同(localhost 🆚 127.0.0.1) |
什么是跨域?
当你访问一个不同源的接口时,浏览器会因为同源策略阻止这个请求,这就是跨域。
通常控制台会出现这样的错误:

如何解决跨域
JSONP(比较老的方法,不推荐)
解决方法:
- 浏览器生成一个script元素,访问数据接口
- 服务器响应一段js代码,调用某个函数,并把响应数据传入
前端代码:
function jsonp(url){
const script = document.createElement("script");
script.src = url;
// 为了不影响页面,script加载过后,将其移除
script.addEventListener("load", ()=>{
script.remove();
)
}
jsonp("请求的url地址");
// 服务器返回的js函数
function callback(data){
console.log(data);
}
服务器端代码:
router.get("/", async (req, res) => {
const result = await stuServ.getStudents();
res.status(200).send(getResult(result));
// jsonp 实现跨域
// 首先将 content-type的值设置为 “application/javascript”
// 修改返回的数据,将数据放入到callback函数中
res.header("content-type", "application/javascript").send(`callback(${JSON.stringify(result)})`);
});
JSONP的问题:
- 会打乱服务器的消息格式:jsonp要求服务器响应js代码,但是在非跨域情况下,服务器又需要响应一个正常的json合适。
- 只能完成GET请求:浏览器的script标签发出的请求,只能是get请求。
CORS (服务器端设置)
CORS是基于http1.1的一种跨域解决方案,它的全称是Cross-Origin Resource Sharing,跨域资源共享。
总体思路:如果浏览器要跨域访问服务器资源,需要获得服务器的允许。
三种不同的交互模式:
-
简单请求
- 需要同时满足的条件:
- 请求方法属于:get,post, head 中的一种
- 请求头仅包含安全的字段,常见安全字段如下:
- Accept
- Accept-language
- Content-Type
- Content-language
- DPR
- DownLink
- Save-Data
- Viewport-Width
- Width
- 请求头如果包含 Content-Type,值只能为:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
- 当浏览器判定某个ajax请求为简单请求时:
-
会在请求头中自动添加
Origin字段,告诉服务器是哪个源地址在跨域请求 -
服务器响应头中应该包含
Access-Control-Allow-Origin,允许跨域请求

代码实现:const allowOrigins = ["origin1", "origin2", ...]; if ("origin" in req.headers && allowOrigins.includes(req.headers.origin)) { res.header("access-control-allow-origin", req.headers.origin); }
-
- 需要同时满足的条件:
-
需要预检的请求
若浏览器判定ajax请求不是一个简单的请求,就会按照下面的流程进行:-
浏览器发送预检请求(OPTIONS)询问服务器是否允许
-
服务器允许
-
浏览器发送真实请求
-
服务器完成真实响应
例如:
有一个需要预检的跨域请求:fetch("http://myRequest.com/api/test", { method: "POST", header: { "Content-Type": "application/json", a: "a", b: "b" }, body: JSON.stringify({ name: "abc", age: 18 }) }).then(res=>res.json()).then(data => console.log(data));此时浏览器会发送一个预检请求,询问服务器是否允许:

预检请求有以下特征:- 请求方法为
OPTIONS - 没有请求体
- 请求头中包含
Origin:请求的源,和简单请求的含义一致Access-Control-Request-Method:后续的真实请求将使用的请求方法Access-Control-Request-Headers:后续的真实请求会改动的请求头
若服务器允许,需要在请求头中添加:
Access-Control-Allow-Method: 允许的后续真实请求Access-Control-Allow-Headers: 允许改动的请求头Access-Control-Allow-Origin:允许的源Access-Control-Max-Age:告诉浏览器,多少秒内,对于同样的请求源、方法、头,都不需要再发送预检请求了
示例代码:
const allowOrigins = ["origin1", "origin2", ...]; if (req.method === "OPTIONS") { res.header("Access-Control-Allow-Methods", req.header("access-control-request-method")); res.header("Access-Control-Allow-Headers", req.header("access-control-request-headers")); } if ("origin" in req.headers && allowOrigins.includes(req.headers.origin)) { res.header("access-control-allow-origin", req.headers.origin); } - 请求方法为
-
-
附带身份凭证的请求
有一些场景,需要请求携带cookie,只需要在响应头中添加:Access-Control-Allow-Credentials: true即可。
对于一个附带身份凭证的请求,若服务器没有明确告知,浏览器仍然视为跨域被拒绝。
note:
对于跨域请求,nodejs中可以使用 cors库来实现,也可以自己封装跨域的中间件。
浏览器跨域配置(开发阶段,测试使用)
谷歌浏览器为例:
1. 新建目录(如C:\MyChromeDevUserData)。
2. 右键快捷方式→属性→在“目标”字段末尾添加 --disable-web-security --user-data-dir=C:\MyChromeDevUserData(注意参数前有空格)。
若原目标值带引号,参数需加在引号外。
webpack/vite 等工程化工具中配置
module.exports = {
// 其他配置...
devServer: {
// 其他devServer配置...
proxy: {
'/api': {
target: 'http://example.com', // 目标服务器地址
changeOrigin: true, // 是否改变源地址
pathRewrite: {'^/api' : ''}, // 重写路径
// 其他代理配置...
}
}
}
};
4475

被折叠的 条评论
为什么被折叠?



