一、跨域核心原理
浏览器同源策略限制:协议、域名、端口三者任意一个不同,即为跨域,浏览器会拦截响应。
同源三要素:http/https、www.a.com、8080
二、主流解决方案(按使用场景分类)
方案 1:CORS 跨域资源共享(后端标准方案,推荐)
后端通过响应头告知浏览器允许跨域,无前端改造,服务端配置。
关键响应头
http
# 允许指定域名,*代表所有域名(生产不推荐)
Access-Control-Allow-Origin: https://xxx.com
# 允许携带Cookie、Token
Access-Control-Allow-Credentials: true
# 允许自定义请求头
Access-Control-Allow-Headers: Content-Type,token
# 允许的请求方法
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
# 预检请求有效期
Access-Control-Max-Age: 3600
不同后端实现示例
SpringBoot Java
java
运行
@CrossOrigin(origins = "https://前端域名", allowCredentials = "true")
全局 CORS 配置类可统一放行。
2. Nginx 反向代理配置 CORS
nginx
add_header Access-Control-Allow-Origin https://xxx.com;
add_header Access-Control-Allow-Credentials true;
Node.js Express
js
运行
app.use(cors({origin:"前端地址",credentials:true}))
预检请求 OPTIONS
复杂请求(带自定义 header、Content-Type 非表单)浏览器先发 OPTIONS 预检,后端必须正常返回 204,否则跨域失败。
方案 2:Nginx 反向代理(前后端分离最常用)
核心思路:同源访问 Nginx,由 Nginx 转发请求到后端,浏览器只和 Nginx 交互,不存在跨域。
适用场景:前端静态资源、后端接口部署在不同服务器。
Nginx 完整配置示例
nginx
server {
listen 80;
server_name fe.xxx.com; # 前端访问域名
# 前端静态资源
location / {
root /static/fe;
index index.html;
}
# 所有/api开头接口转发到后端服务
location /api/ {
proxy_pass http://127.0.0.1:8090; # 后端真实地址
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
优势:不用改后端代码,统一处理跨域、负载均衡、SSL 证书。
方案 3:JSONP(仅支持 GET 请求,老旧兼容方案,不推荐)
原理:<script>标签不受同源策略限制,通过动态创建 script 标签请求接口,后端返回包裹回调函数的 JS 代码。
前端示例
js
运行
function callback(res) {
console.log("返回数据", res);
}
// 动态创建script
const script = document.createElement('script');
script.src = 'http://后端域名/api/data?callback=callback';
document.body.appendChild(script);
后端返回格式
js
运行
callback({"code":200,"data":"测试数据"})
缺点:
仅 GET 请求,无法 POST/PUT
存在 XSS 安全风险
不支持携带 Cookie
现代项目基本淘汰,仅维护老系统时使用。
方案 4:PostMessage(页面间跨域通信:iframe、新开窗口)
适用于:父页面与 iframe 嵌套页面、window.open 新开页面跨域传参。
API:window.postMessage(data, targetOrigin)
示例
父页面(a.com)发送消息
js
运行
const iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage({type:"getData"}, "https://b.com");
iframe 子页面(b.com)监听
js
运行
window.addEventListener('message', (e) => {
// e.origin校验来源,防止恶意网站
if(e.origin !== "https://a.com") return;
console.log(e.data);
// 回复数据
e.source.postMessage({res:"返回内容"}, e.origin);
})
优势:支持复杂数据、双向通信;仅解决页面嵌入场景,不适用接口请求跨域。
方案 5:WebSocket(实时通信无跨域限制)
WebSocket 不受同源策略约束,浏览器不会拦截,适合聊天、推送实时业务。
前端连接:
js
运行
const ws = new WebSocket("ws://后端域名:9000");
注意:WebSocket 握手阶段仍会走 HTTP,如需携带 Cookie,后端仍需配置 CORS。
方案 6:浏览器本地关闭同源策略(仅本地开发调试,严禁生产)
开发时前端本地localhost:5173,后端localhost:8080跨域,临时方案:
Chrome 关闭同源策略
关闭所有 Chrome 窗口
终端执行命令:
bash
# Windows
chrome --disable-web-security --user-data-dir="D:\chrome_dev"
# Mac
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --disable-web-security --user-data-dir="/tmp/chrome_dev"
打开后提示「安全模式已禁用」,仅本地调试,存在严重安全风险,生产绝对不能使用。
方案 7:Vite/Webpack 开发代理(前端本地开发专用)
仅作用于本地开发环境,打包上线失效;原理是启动 node 中间件转发请求,规避浏览器跨域。
Vite 配置 vite.config.js
js
运行
export default defineConfig({
server: {
proxy: {
// 匹配/api开头接口
'/api': {
target: 'http://127.0.0.1:8090', // 后端地址
changeOrigin: true, // 修改源,模拟同源
rewrite: (path) => path.replace(/^\/api/, '') // 去除/api前缀
}
}
}
})
Webpack Vue CLI vue.config.js
js
运行
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8090',
changeOrigin: true,
pathRewrite: {'^/api': ''}
}
}
}
}
重点:打包部署到服务器后,该代理配置无效,线上仍需要 CORS 或 Nginx 反向代理。
三、方案选型对比表
四、常见踩坑点
CORS 中
Access-Control-Allow-Origin: *不能搭配Access-Control-Allow-Credentials: true;自定义 Token 请求头会触发 OPTIONS 预检,后端必须正常响应 OPTIONS;
开发代理仅本地生效,上线忘记配置 Nginx/CORS 会出现线上跨域;
携带 Cookie 跨域时,前端请求需要配置
withCredentials: true。