这几天在调试网站的弹幕功能的过程中遇到了下面的问题,不太懂前端的鼠鼠看的一脸懵

时隔一年多,再次更新这篇文章。随着对前端技术的学习,对跨域问题有了更清晰的认识。当初撰写此文时,受限于前端知识的不足,只是记录了问题,并附上了别人的解决博客。如今,我将系统地补充「跨域问题及其核心解决方案」,希望能为读者提供更透彻的解析和实践指南。
跨域问题及解决方案
浏览器同源策略:安全基石,也是跨域问题的根源
一、什么是跨域问题?
跨域(Cross-Origin)指的是浏览器禁止当前页面向不同协议(protocol)、域名(domain)或端口(port) 的 URL 发起请求的安全限制。这是浏览器核心安全策略——同源策略(Same-Origin Policy) 的直接体现。
1 | http://www.example.com:80/dir/page.html |
三者必须完全相同,上述任意一项不同,都算作跨域。
举例说明:
| 请求URL | 目标URL | 是否跨域 | 原因 |
|---|---|---|---|
https://a.com |
https://a.com/api |
否 | 同源 |
http://a.com |
https://a.com |
是 | 协议不同 |
http://a.com |
http://b.com |
是 | 域名不同 |
http://a.com:80 |
http://a.com:8080 |
是 | 端口不同 |
http://b.a.com |
http://api.a.com |
是 | 子域名不同 |
为什么会出现跨域问题?
浏览器为了保护用户的信息安全,不允许任意网页访问另一个域上的敏感数据,从而防止常见的 CSRF、XSS 等攻击。
跨域请求如果不加以限制,就可能导致恶意脚本读取用户在其它站点的敏感数据(如 Cookies、LocalStorage)。
二、为什么需要同源策略?
同源策略(Same‑Origin Policy,SOP)是浏览器的一项安全机制,规定了来自同一源(协议、域名、端口三者都相同)的脚本,才能互相访问彼此的资源。
同源策略可以:
- 防范 CSRF、XSS 等攻击:限制脚本只能对同源资源进行操作,降低跨站攻击面。
- 保护用户隐私:确保只有受信任的页面能获取和修改本域的 Cookie/LocalStorage。
- 防止数据泄露:阻止恶意脚本读取用户在其它站点的敏感数据。
- 隔离潜在威胁:限制不同源之间的DOM访问和操作
三、常见的跨域场景
前后端分离
前端页面部署在 http://localhost:3000 后端接口在 http://api.example.comCDN 与主站点
静态资源如图片、脚本、样式文件托管在 https://cdn.example.com ,主站在 https://www.example.com第三方 API 调用
网页需要调用 https://api.thirdparty.com 提供的地图、支付、社交登录接口。跨端口访问
同域名但不同端口:http://example.com:8000 与 http://example.com:3000嵌入式 iframe
父页面与嵌入的 iframe 内容在不同域名下,需要交互数据。
四、解决方案
1. CORS(跨域资源共享)⭐️ 现代首选方案
原理:浏览器自动实现的W3C标准,需要服务端配合设置响应头。CORS 通过服务器在响应头里添加一系列标识,告诉浏览器哪些跨域请求被允许。
基本响应头
1
2
3
4
5Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600简单请求 vs 预检请求
- 简单请求:GET/POST(
Content-Type为application/x-www-form-urlencoded、multipart/form-data或text/plain) - 预检请求:对于其他方法或自定义头部,浏览器会先发送
OPTIONS请求,服务器返回上述头部后才正式发起主请求。
- 简单请求:GET/POST(
优点
- 标准化、浏览器原生支持
- 支持带凭证请求(
withCredentials=true)
缺点
- 需要后端代码或配置支持
- 部分旧浏览器兼容性差
服务端配置示例(Node.js):
1 | // 允许的跨域来源白名单(可维护多个域名) |
客户端处理:
1 | fetch('https://api.example.com/data', { |
2. 反向代理 ⭐️ 开发环境首选
原理:前端向同源的代理服务器发请求,代理服务器再转发到目标域,最后将结果返回给前端。对浏览器来说始终是同源请求。
前端开发服务器(如Webpack)或Nginx代理请求
Webpack配置示例:
1 | // vue.config.js / webpack.config.js |
Nginx配置:
1 | server { |
优点
- 无跨域限制
- 可隐藏真实接口地址,提高安全性
- 可统一做鉴权、日志、限流等
缺点
- 部署成本较高,需要额外的服务器或配置
- 增加网络跳数,可能带来性能开销
3. JSONP(历史方案)⚠️ 仅限GET请求
⚠️ JSONP 已基本被 CORS 所取代,仅在兼容老浏览器或特殊场景下使用。
利用 <script> 标签不受同源策略限制的特点,通过动态创建 <script src="...">,并约定回调函数名,将服务器返回的 JSON 数据包裹在函数调用里。
服务端:
1 | app.get('/data', (req, res) => { |
客户端:
1 | function handleJSONP(data) { |
优点
- 简单,无需浏览器额外设置
- 支持老旧浏览器
缺点
- 只支持
GET请求 - 存在一定安全隐患(XSS 风险)
- 无法访问响应头
4. WebSocket协议
全双工通信协议,不受同源策略限制,但仍建议服务端校验 Origin 请求头,以防止非法的跨站连接。
客户端:
1 | const socket = new WebSocket('wss://api.example.com'); |
优点
- 双向通信,无需频繁轮询
缺点
- 需服务端特别处理
- 浏览器兼容需关注
5. postMessage API ⭐️ 跨窗口通信
- 跨域的父子页面(iframe)之间通信
- WebWorker 与主线程间通信
原理:通过 window.postMessage() 发送消息,目标窗口通过 message 事件监听接收,并验证来源。
主页面:
1 | // 向iframe发送消息 |
iframe页面:
1 | window.addEventListener('message', (event) => { |
优点
- 灵活,支持各种消息格式
- 安全,需显式验证
origin
缺点
- 仅限于窗口间或 Worker 通信,不适用于普通 AJAX
五、方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| CORS | 现代API调用 | 标准安全、支持所有HTTP方法 | 需要服务端改造 |
| 反向代理 | 本地开发、同域部署 | 前端无感知、无缝切换环境 | 生产环境需运维配合 |
| JSONP | 旧浏览器兼容 | 支持老式浏览器 | 仅GET、安全性低 |
| WebSocket | 实时双向通信 | 高性能、全双工 | 非HTTP协议、复杂度高 |
| postMessage | 跨窗口通信 | 安全可控、支持跨域 | 仅限窗口间通信 |
六、注意事项
CORS配置风险:
- 避免滥用
Access-Control-Allow-Origin: * - 生产环境应指定精确域名
- 敏感接口需要验证
Origin头
- 避免滥用
CSRF防护:
1
2
3
4
5
6
7
8
9
10
11// 服务端验证示例
app.post('/transfer', (req, res) => {
const origin = req.headers.origin;
const allowedOrigins = ['https://trusted-site.com'];
if (!allowedOrigins.includes(origin)) {
return res.status(403).send('Forbidden');
}
// 处理业务逻辑...
});Cookie安全:
- 使用
SameSite属性限制Cookie - 敏感操作增加二次验证
- 使用
总结
推荐优先采用CORS+反向代理的组合方案,开发阶段通过代理解决跨域,部署阶段启用后端 CORS 支持,兼顾开发效率与生产安全。随着Web技术的发展,更优雅的跨域解决方案正在不断涌现,开发者应持续关注。
推荐阅读: