如何解决前端跨域问题
如何解决前端跨域问题
只要你做过前后端分离,就几乎一定遇到过这个报错:
1 | |
这就是经典的“跨域问题”。本文主要解决三个问题:
- 什么是“同源策略”?什么是“跨域”?
- 浏览器为什么要限制跨域?哪些操作会被限制?
- 工程中有哪些常用的跨域解决方案?(CORS、代理、JSONP、postMessage 等)
一、什么是同源策略与跨域?
1. 同源策略(Same-Origin Policy)
同源:协议、域名、端口号三者都相同。
例如:
https://a.com:443/page与https://a.com:443/api→ 同源http://a.com与https://a.com→ 不同源(协议不同)https://a.com与https://b.com→ 不同源(域名不同)https://a.com:443与https://a.com:8443→ 不同源(端口不同)
同源策略是浏览器的一个重要安全基石,它限制以下行为:
- 读取非同源页面的 DOM
- 获取非同源接口的响应内容(XHR/fetch)
- 读写非同源的 Cookie、LocalStorage、IndexDB 等
注意:
- 请求本身是会发出去的(网络上可以看到),但浏览器会拦截响应结果,不允许 JS 访问。
2. 什么算“跨域”?
只要发起的请求目标与当前页面的“源”不同,就是跨域请求。
常见场景:
- 本地开发:
http://localhost:3000调http://localhost:8080 - 前端部署在
web.xxx.com,后端在api.xxx.com
二、浏览器的跨域限制具体表现在哪?
1. XHR / fetch
1 | |
如果目标域未正确设置 CORS 响应头,浏览器会:
- 发起网络请求
- 收到响应
- 但在 JS 层抛出 CORS 错误,不允许访问响应体
2. 表单提交与图片/脚本等标签
注意:
<form>提交、<img>、<script>、<link>等标签本身不受同源策略限制(请求可以发,响应内容不会被 JS 直接访问)- 但
<script>加载跨域脚本有自己的安全约束(如 CSP、SRI)
三、CORS:最标准的跨域解决方案
CORS(Cross-Origin Resource Sharing) 是 W3C 标准,允许服务器通过响应头控制哪些源可以访问资源。
1. 简单请求 vs 预检请求
简单请求(Simple Request)
满足以下条件:
- 方法是 GET / HEAD / POST
- 且 自定义头受限(常见头:
Accept、Accept-Language、Content-Language、Content-Type仅限text/plain、multipart/form-data、application/x-www-form-urlencoded)
浏览器会直接发送请求,并根据响应头判定是否允许 JS 访问结果。
预检请求(Preflight)
如果不满足“简单请求”条件(如使用 PUT/DELETE、自定义头、application/json 等),浏览器会在正式请求前:
- 先发一个
OPTIONS请求作为“预检” - 服务端在预检响应中声明是否允许该跨域请求
2. 核心响应头
示例:
1 | |
含义:
Access-Control-Allow-Origin:允许访问的源- 不能为
*的情况下再配合Allow-Credentials: true
- 不能为
Access-Control-Allow-Credentials:是否允许携带 CookieAccess-Control-Allow-Methods:允许的 HTTP 方法Access-Control-Allow-Headers:允许的自定义请求头Access-Control-Max-Age:预检结果的缓存时间(秒)
3. 前端 fetch 配置
1 | |
注意:
- 如果
credentials: include,则服务端:Access-Control-Allow-Origin不能是*,必须是具体域名- 必须加上
Access-Control-Allow-Credentials: true
四、开发环境常用:代理转发解决跨域
在本地开发时,通常不希望修改后端配置,可以通过“开发服务器代理”来规避跨域。
原理:
- 浏览器 → 本地开发服务器(同源) → 代理转发到后端 API
- 对浏览器来说,始终是同源请求
1. Vite 示例
1 | |
前端请求:
1 | |
2. Webpack devServer 示例
1 | |
这种“代理转发”方案只适用于开发环境或自有网关服务,不适用于用户浏览器直接访问的线上环境。
五、JSONP(老方案,了解即可)
原理利用了 <script> 标签可以跨域加载脚本这一特性:
- 前端定义一个全局回调函数,如
window.handleData - 在页面中插入
<script src="https://api.xxx.com/jsonp?callback=handleData"> - 服务器返回一段 JS 代码:
handleData({ /* 数据 */ })
局限:
- 只能用
GET请求 - 不安全(执行远程脚本)
- 不方便处理错误与状态码
现代项目中已很少使用,除非与老系统兼容。
六、postMessage:跨窗口/iframe 通讯
当你的页面与其他源的页面通过 iframe 或新窗口协作时,可以使用 window.postMessage:
1 | |
注意:
- 始终校验
event.origin,避免接受来自恶意页面的消息。
postMessage 并不是解决 XHR/fetch 跨域问题的方案,而是解决页面间通信的方案。
七、Nginx 反向代理 + 同域部署
在生产环境中,常见做法是使用 Nginx 等网关:
- 将前端静态资源与后端 API 部署在同一主域名下的不同路径
- 通过 Nginx 的
location转发到不同服务
示例:
1 | |
对浏览器而言,请求都来自 https://example.com,不存在跨域问题。
八、不同场景下的方案选择
| 场景 | 推荐方案 |
|---|---|
| 正式环境,前后端分离,域名不同 | 首选 CORS,后端统一配置;或通过反向代理统一为同域 |
| 开发环境,本地端口不同 | 开发服务器 代理转发(Vite/Webpack devServer) |
| 老接口,仅支持 JSONP | 使用 JSONP 封装,或在服务端增加 CORS 支持 |
| 跨域页面通信(iframe/新窗口) | window.postMessage |
九、总结
“跨域问题”本质上是浏览器的同源策略在起作用,目的是保护用户安全,而不是“为难前端”:
- 请求可以发送,但响应内容是否可被 JS 访问由浏览器控制
- CORS 是标准且现代的解决方案,核心在于服务器通过响应头显式声明允许哪些源访问
- 开发环境中,我们可以用代理转发来规避跨域限制,加快开发效率
真正理解同源策略与 CORS 的原理后,面对各类“跨域错误”就不再是靠“百度配置复制粘贴”,而是能根据项目架构、环境与安全要求,选择最合适的解决方案。