文章目录
- 前言
- 一、同源策略:浏览器的"安全门卫"
- 1.1 什么是同源?先搞懂"三兄弟"
- 1.2 浏览器为啥要搞同源策略?安全第一!
- 二、跨域本质:浏览器拦的,不是服务器
- 三、2026年主流跨域解决方案(全场景覆盖)
- 3.1 CORS:现代Web跨域"标准之王"(最推荐)
- 3.1.1 什么是CORS?
- 3.1.2 CORS工作流程(极简版)
- 3.1.3 简单请求 vs 预检请求(OPTIONS)
- 3.1.4 核心响应头(必须掌握)
- 3.1.5 实战代码(Express+前端)
- 3.2 代理服务器:开发/生产"万能钥匙"
- 3.2.1 原理(最容易理解)
- 3.2.2 开发环境:Vite/Webpack代理(神器)
- 3.2.3 生产环境:Nginx反向代理(大厂标准)
- 3.3 JSONP:兼容老浏览器的"上古方案"(了解即可)
- 3.3.1 原理
- 3.3.2 实战代码
- 3.4 postMessage:iframe/跨窗口通信"专用方案"
- 3.4.1 适用场景
- 3.4.2 实战代码
- 3.5 其他方案(了解)
- 3.5.1 document.domain:主域相同子域跨域
- 3.5.2 WebSocket:实时通信无跨域
- 3.5.3 window.name/location.hash:过时方案
- 四、方案选型指南(2026年最佳实践)
- 4.1 开发环境(本地调试)
- 4.2 生产环境(自有服务、前后端分离)
- 4.3 iframe/多窗口跨域通信
- 4.4 调用第三方接口(不可控)
- 4.5 绝对禁忌(生产环境)
- 五、常见跨域坑与避坑指南(2026最新)
- 5.1 带Cookie跨域失败(最常见)
- 5.2 自定义请求头触发OPTIONS预检
- 5.3 端口号被忽略(本地开发)
- 5.4 Nginx代理后Cookie丢失
- 5.5 浏览器缓存导致跨域异常
- 六、总结:跨域其实很简单
P.S. 目前国内还是很缺AI人才的,希望更多人能真正加入到AI行业,共同促进行业进步,增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 http://blog.csdn.net/jiangjunshow,教程通俗易懂,高中生都能看懂,还有各种段子风趣幽默,从深度学习基础原理到各领域实战应用都有讲解,我22年的AI积累全在里面了。注意,教程仅限真正想入门AI的朋友,否则看看零散的博文就够了。
前言
老铁们,做前端开发的,谁没被跨域坑过?我敢说,只要你写过AJAX、调过接口、玩过前后端分离,跨域这两个字绝对是你开发路上的“拦路虎”。控制台那一句经典的No 'Access-Control-Allow-Origin' header is present on the requested resource,能让多少刚入行的小伙伴直接破防,怀疑人生。
我干开发22年,从早年的ASP、PHP混写,到后来的jQuery时代,再到现在的Vue、React三大框架横行,跨域问题就像狗皮膏药一样,一直跟着Web开发。很多人知其然不知其所以然,只知道CORS、JSONP、Nginx代理这些名词,却不懂浏览器为啥要搞出同源策略,各种方案的本质区别是什么,生产环境该怎么选才最稳妥、最安全。
今天这篇文章,我就用最通俗、最接地气的话,把同源策略的来龙去脉、跨域的本质、以及2026年最新、最实用的解决方案,一次性给你讲透。保证你看完不仅能解决日常开发的跨域坑,面试的时候也能吹得面试官一愣一愣的,直接拿捏。
一、同源策略:浏览器的"安全门卫"
1.1 什么是同源?先搞懂"三兄弟"
要懂跨域,先懂同源。同源(Same-Origin)是浏览器安全的基石,判断两个URL是否同源,只看三个东西:协议、域名、端口。三者必须完全一致,差一个字母、一个符号、一个数字,都算跨域。
我给你举几个例子,一看就懂:
- 当前页面:
https://www.baidu.com/index.htmlhttps://www.baidu.com/api/user✅ 同源(协议、域名、端口全一样)http://www.baidu.com/api/user❌ 不同源(协议不同:https vs http)https://map.baidu.com/api❌ 不同源(子域名不同:www vs map)https://www.baidu.com:8080/api❌ 不同源(端口不同:默认443 vs 8080)https://baidu.com/api❌ 不同源(域名不同:www.baidu.com vs baidu.com)
简单记:协议、域名、端口,一个都不能少。少一个、变一个,浏览器就不认你是自己人。
1.2 浏览器为啥要搞同源策略?安全第一!
很多新手吐槽:同源策略太烦了,限制这限制那,直接关掉不香吗?
大错特错!同源策略是浏览器的"安全门卫",没有它,你上网就等于裸奔。
我给你打个比方:
你登录了银行网站(bank.com),Cookie里存了登录态。这时候你不小心点开一个恶意网站(hacker.com)。如果没有同源策略,恶意网站的JS就能直接读取你银行网站的Cookie、获取你的账户信息、甚至偷偷发起转账请求——你的钱瞬间就没了。
同源策略的核心目的,就是隔离不同源的资源,防止恶意网站窃取数据、伪造请求、篡改DOM。它主要限制三件事:
- AJAX/Fetch请求限制:不同源的接口,直接请求会被浏览器拦截,控制台报错
- DOM访问限制:不同源的iframe/窗口,不能互相读取DOM、调用方法
- 存储访问限制:不同源不能读取对方的Cookie、LocalStorage、IndexedDB
一句话总结:同源策略是浏览器的安全底线,跨域问题是安全与开发便利的矛盾产物。我们要做的,不是打破安全,而是在安全框架内,找到合法合规的"通行证"。
二、跨域本质:浏览器拦的,不是服务器
这里有个超级关键的点,90%的新手都搞混:跨域拦截,是浏览器干的,跟服务器没关系!
我再强调一遍:
- 服务器收到跨域请求,正常处理、正常返回数据
- 浏览器拿到服务器返回的结果,检查是否符合同源策略
- 不符合,浏览器直接把数据扔掉,抛给你一个跨域错误
- 服务器全程不知道自己的响应被浏览器丢了
这就好比:
你(浏览器)去快递站(服务器)取快递(数据)。快递站不管你是谁,把包裹给你了。但你回家路上,被小区保安(同源策略)拦住了:“这个包裹不是你家(同源)的,不能带进小区!” 于是你只能把包裹扔了,还跟主人(前端代码)说:“取不到,被拦了!”
所以:
- 服务器没毛病,响应正常
- 问题出在浏览器的安全校验
- 解决跨域,本质就是让浏览器知道:这个跨域请求是合法的、被允许的
三、2026年主流跨域解决方案(全场景覆盖)
3.1 CORS:现代Web跨域"标准之王"(最推荐)
3.1.1 什么是CORS?
CORS(Cross-Origin Resource Sharing,跨域资源共享),是W3C官方标准,2026年开发首选、最规范、最安全的跨域方案。
原理一句话:服务器通过HTTP响应头,告诉浏览器:“这个来源我允许访问,放行!”
3.1.2 CORS工作流程(极简版)
- 浏览器发起跨域请求,自动在请求头加
Origin: 当前域名 - 服务器收到请求,检查Origin是否在白名单
- 允许:返回
Access-Control-Allow-Origin: 允许的域名 - 浏览器检查响应头,匹配成功:把数据给前端
- 不允许:浏览器拦截,抛跨域错误
3.1.3 简单请求 vs 预检请求(OPTIONS)
CORS分两种请求,面试必考:
① 简单请求(满足两个条件)
- 方法:GET/HEAD/POST
- 请求头只有:Accept、Accept-Language、Content-Language、Content-Type(仅限application/x-www-form-urlencoded、multipart/form-data、text/plain)
特点:直接发请求,服务器返回CORS头即可。
② 非简单请求(PUT/DELETE/自定义头/Content-Type:application/json)
特点:浏览器先发OPTIONS预检请求,问服务器:“我要发XX方法、带XX头,你允许吗?”
服务器同意:返回204,浏览器再发真实请求
服务器拒绝:直接拦截,真实请求不发
3.1.4 核心响应头(必须掌握)
# 允许的源(生产别用*,要指定具体域名) Access-Control-Allow-Origin: https://www.your-frontend.com # 允许的请求方法 Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS # 允许的请求头 Access-Control-Allow-Headers: Content-Type,Authorization,Token # 是否允许携带Cookie(true时,Allow-Origin不能用*) Access-Control-Allow-Credentials: true # 预检缓存时间(秒),减少OPTIONS请求 Access-Control-Max-Age: 864003.1.5 实战代码(Express+前端)
后端(Express)
// 全局CORS中间件(生产建议用cors库)app.use((req,res,next)=>{// 允许的前端域名constallowOrigin=['https://www.a.com','https://www.b.com']constorigin=req.headers.originif(allowOrigin.includes(origin)){res.header('Access-Control-Allow-Origin',origin)}res.header('Access-Control-Allow-Methods','GET,POST,PUT,DELETE,OPTIONS')res.header('Access-Control-Allow-Headers','Content-Type,Authorization')res.header('Access-Control-Allow-Credentials','true')// 处理OPTIONS预检if(req.method==='OPTIONS'){returnres.sendStatus(204)}next()})前端(带Cookie请求)
// fetchfetch('https://api.your-backend.com/user',{method:'GET',credentials:'include'// 关键:携带Cookie})// axiosaxios.get('https://api.your-backend.com/user',{withCredentials:true// 关键})优点:标准、支持所有请求、安全、可控
缺点:必须后端配合配置
适用:前后端分离、自有服务、生产环境首选
3.2 代理服务器:开发/生产"万能钥匙"
3.2.1 原理(最容易理解)
同源策略只限制浏览器,服务器之间通信没有跨域。
代理方案:
- 前端 → 请求同源代理地址(如
/api/xxx) - 代理服务器(本地/Vite/Nginx)→ 转发到真实后端
- 代理服务器拿到响应 → 返回给前端
- 浏览器以为是同源请求,直接放行
3.2.2 开发环境:Vite/Webpack代理(神器)
Vite配置(2026最主流)
// vite.config.jsexportdefault{server:{proxy:{'/api':{target:'https://api.your-backend.com',// 真实后端changeOrigin:true,// 改Host头rewrite:(path)=>path.replace(/^\/api/,'')// 路径重写(可选)}}}}前端直接请求/api/user,浏览器不跨域,开发爽到爆。
3.2.3 生产环境:Nginx反向代理(大厂标准)
线上项目,几乎都用Nginx统一入口,彻底消灭跨域。
Nginx配置
server { listen 80; server_name www.your-site.com; # 前端静态文件 location / { root /usr/share/nginx/html; index index.html; } # 接口代理到后端 location /api { proxy_pass https://api.your-backend.com; # 真实后端 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }用户访问www.your-site.com/api/user,Nginx转发到后端,浏览器全程以为是同源。
优点:前端无感知、彻底解决跨域、安全、可加限流/负载均衡
缺点:需配置代理服务器
适用:开发环境调试、生产环境统一入口
3.3 JSONP:兼容老浏览器的"上古方案"(了解即可)
3.3.1 原理
利用<script>标签不受同源策略限制的特性:
- 前端动态创建
<script src="https://api.com?callback=handleData"> - 服务器返回:
handleData({ name:"张三" })(JS函数调用) - 浏览器加载执行,前端拿到数据
3.3.2 实战代码
// 前端functionhandleData(data){console.log('拿到数据:',data)}constscript=document.createElement('script')script.src='https://api.your-backend.com/data?callback=handleData'document.body.appendChild(script)优点:兼容IE6-8(2026几乎不用)
缺点:仅支持GET、不安全、无法发POST/PUT、无法处理错误
适用:极老系统、第三方不支持CORS的接口(极少)
3.4 postMessage:iframe/跨窗口通信"专用方案"
3.4.1 适用场景
页面嵌套iframe、多窗口(window.open)之间跨域通信。
3.4.2 实战代码
父页面(a.com)
// 向子iframe发消息constiframe=document.getElementById('child-iframe')iframe.contentWindow.postMessage({type:'hello',data:'来自父页面'},'https://b.com'// 必须指定子页面域名,安全!)// 监听子页面返回window.addEventListener('message',(e)=>{// 安全校验:必须验证来源if(e.origin!=='https://b.com')returnconsole.log('子页面消息:',e.data)})子iframe页面(b.com)
// 监听父页面消息window.addEventListener('message',(e)=>{if(e.origin!=='https://a.com')returnconsole.log('父页面消息:',e.data)// 回复消息e.source.postMessage({type:'reply',data:'收到了'},e.origin)})安全铁律:
- 生产环境绝对不能用
targetOrigin: * - 必须校验
event.origin,只信任指定域名
优点:HTML5标准、安全、双向通信
缺点:仅适用于窗口/iframe场景
适用:iframe嵌套、多窗口跨域通信
3.5 其他方案(了解)
3.5.1 document.domain:主域相同子域跨域
适用:a.xxx.com和b.xxx.com
// 两个页面都设置document.domain='xxx.com'即可互相访问DOM/Cookie。
3.5.2 WebSocket:实时通信无跨域
WebSocket协议不受同源策略限制,可直接跨域通信。
constws=newWebSocket('wss://api.your-backend.com/ws')适合聊天室、直播、实时数据推送。
3.5.3 window.name/location.hash:过时方案
兼容性差、不安全、功能有限,2026年基本淘汰,了解即可。
四、方案选型指南(2026年最佳实践)
讲了这么多方案,很多人懵了:我到底该用哪个?
直接给你2026年最实用、最稳妥的选型结论,照着选不出错:
4.1 开发环境(本地调试)
首选:Vite/Webpack 代理
理由:前端一键配置、无需后端配合、开发效率拉满、零成本解决跨域。
4.2 生产环境(自有服务、前后端分离)
首选:CORS(后端配置)
理由:W3C标准、安全可控、支持所有请求、性能最好、无中间层损耗。
备选:Nginx反向代理
理由:后端不方便改配置、需要统一入口、隐藏后端地址、加负载均衡/限流。
4.3 iframe/多窗口跨域通信
唯一选择:postMessage
理由:HTML5标准、安全、官方推荐、双向通信。
4.4 调用第三方接口(不可控)
- 支持CORS:直接用CORS
- 不支持CORS:自己服务器搭一层代理(前端→你的代理→第三方接口),绝对别用JSONP。
4.5 绝对禁忌(生产环境)
- 禁止CORS用
Access-Control-Allow-Origin: *(带Cookie时完全失效、不安全) - 禁止postMessage用
targetOrigin: *(极易被劫持) - 禁止生产环境用JSONP(仅GET、不安全)
- 禁止随意关闭浏览器同源策略(本地调试偶尔用,线上绝对不行)
五、常见跨域坑与避坑指南(2026最新)
5.1 带Cookie跨域失败(最常见)
原因三要素:
- 后端
Access-Control-Allow-Credentials: true - 后端
Access-Control-Allow-Origin必须指定具体域名,不能用* - 前端请求必须带
credentials: include(fetch)或withCredentials: true(axios)
5.2 自定义请求头触发OPTIONS预检
加了Token/Authorization等自定义头,浏览器自动发OPTIONS。
解决:后端配置Access-Control-Allow-Headers包含自定义头,并处理OPTIONS请求。
5.3 端口号被忽略(本地开发)
本地:localhost:3000前端 →localhost:8080后端
端口不同算跨域,必须用代理或CORS。
5.4 Nginx代理后Cookie丢失
解决:Nginx加配置
proxy_cookie_domain backend.com your-site.com; proxy_set_header Cookie $http_cookie;5.5 浏览器缓存导致跨域异常
解决:清浏览器缓存、禁用缓存、加Cache-Control: no-cache。
六、总结:跨域其实很简单
最后,用几句话把全文浓缩,方便你记忆:
- 同源 = 协议+域名+端口完全相同,浏览器安全基石
- 跨域拦截是浏览器干的,服务器正常返回,浏览器扔结果
- 2026首选方案:开发用代理、生产用CORS、iframe用postMessage
- 安全第一:生产别用
*、校验origin、不用JSONP - 跨域不是难题,懂原理、选对方案、规范配置,轻松搞定
跨域是前端开发的基础中的基础,也是面试高频考点。把这篇文章吃透,日常开发的跨域坑基本都能踩平,面试也能从容应对。
Web开发日新月异,但同源策略和跨域的核心原理永远不变。掌握本质,才能以不变应万变。
P.S. 目前国内还是很缺AI人才的,希望更多人能真正加入到AI行业,共同促进行业进步,增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 http://blog.csdn.net/jiangjunshow,教程通俗易懂,高中生都能看懂,还有各种段子风趣幽默,从深度学习基础原理到各领域实战应用都有讲解,我22年的AI积累全在里面了。注意,教程仅限真正想入门AI的朋友,否则看看零散的博文就够了。