休闲

web跨域

时间:2010-12-5 17:23:32  作者:知识   来源:探索  查看:  评论:0
内容摘要:浏览器里跑着 localhost:3000 的前端页面,用 fetch 去请求 localhost:8080 的接口,控制台立刻甩出一行红字:Access to fetch at 'http://lo

浏览器里跑着 localhost:3000 的前端页面,用 fetch 去请求 localhost:8080 的接口,控制台立刻甩出一行红字:Access to fetch at 'http://localhost:8080/api' from origin 'http://localhost:3000' has been blocked by CORS policy。端口不同,浏览器就认定这是两个世界。协议、域名、端口,三样里随便动一个,同源策略立刻翻脸。

这事儿的根源不在后端,在后端返回的响应头里。浏览器发请求之前,会先看你要跨的那个域有没有明确说“我允许你”。没说?那就不给看。哪怕服务器已经把数据吐出来了,浏览器也会在 JS 层面把响应体掐掉,只留一个空壳给 fetch。挺流氓的,但这就是安全模型。

很多人第一次踩坑是在本地联调。后端同事说接口没问题,Postman 跑得通,你这边就是 403。因为 Postman 不是浏览器,它不执行同源策略。浏览器里跨域失败,请求其实发出去了,服务器也响应了,只是响应被浏览器拦截。打开 Network 面板能看到状态码 200,但 Response 是空的——这种“假成功”最让人抓狂。

解决路径分两种。开发阶段最省事的是代理。Vite 里配个 server.proxy,把 /api 转发到后端,浏览器眼里所有请求都同源。webpack devServer 也有 proxy 字段,一个道理。代理的本质是让 Node 中间层替浏览器去跨域,Node 没有同源限制。

生产环境就得靠 CORS 头了。后端加 Access-Control-Allow-Origin,值可以是 * 或具体域名。带 Cookie 的请求不能写 *,必须指定域名,同时加 Access-Control-Allow-Credentials: true。前端 fetch 也要带上 credentials: 'include',少一个都白搭。

复杂请求会触发预检。比如你自定义了一个 X-Token 头,或者 Content-Type 是 application/json,浏览器先发一个 OPTIONS 请求问服务器:我能不能用这些方法和头?服务器得回 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers,并且 OPTIONS 请求本身要返回 2xx。很多框架默认不处理 OPTIONS,预检直接挂掉,正式请求根本发不出去。

有个细节容易忽略:Access-Control-Max-Age 可以缓存预检结果,单位是秒。设成 86400,一天内同一组跨域请求不用反复发 OPTIONS。不设的话,每次请求都多一个往返,性能肉眼可见地掉。

JSONP 是老古董了,利用 <script> 标签不受同源限制的特性,靠回调函数拿数据。只能发 GET,错误处理约等于没有,现在基本只在维护祖传代码时见到。iframe 加 document.domain 那套也过时了,现代浏览器对 document.domain 的写入越来越严格。

postMessage 倒是还在用,适合窗口间通信。父页面和 iframe 跨域,通过 window.postMessage 传消息,接收方监听 message 事件。但得校验 event.origin,不然任何页面都能往你这里塞数据,等于自己开了个后门。

WebSocket 不走同源策略那套。握手阶段带 Origin 头,服务器可以自己决定要不要拒绝。一旦连接建立,就是全双工通道,没有 CORS 什么事。所以实时通信场景里,WebSocket 反而比 HTTP 跨域省心。

说到底,跨域不是 bug,是浏览器故意设计的墙。理解它拦的是什么、放行需要什么条件,比背一堆解决方案有用。下次再看到 CORS 报错,先看 Network 里 OPTIONS 请求的响应头,八成问题就出在那儿。

最近更新
热门排行
copyright © 2026 powered by 烟消火灭网   sitemap