为何fetch请求同URL报CORS错误,动态插入script标签却正常?
跨域请求表现差异的核心原因
问题中提到的动态注入script标签的实现代码如下:
((global, url) => { let s = document.createElement('script') s.src = url global.document.body.appendChild(s) })(window, 'https://example.com')
同样请求https://example.com,动态script标签可以正常加载执行脚本,fetch调用却触发CORS错误,本质是浏览器对两类不同用途的资源请求,施加了完全不同的跨域安全策略,和URL本身没有关系。
动态注入script标签的请求逻辑
动态插入script标签加载资源,属于浏览器从Web诞生初期就支持的跨源资源嵌入能力:
- 这类能力的设计初衷是允许页面嵌入跨域的脚本、图片、样式等可直接被浏览器渲染/执行的资源,前端JS没有权限读取这类请求的响应原始文本
- 代码执行后浏览器会正常向目标地址发起GET请求,不会提前做跨域拦截
- 服务端返回响应后,浏览器会直接把响应内容当作JavaScript脚本在当前页面上下文执行,整个过程不需要服务端返回
Access-Control-Allow-Origin这类CORS相关响应头 - 早年广泛使用的JSONP跨域方案,就是基于这个特性实现的
fetch发起请求的校验逻辑
fetch属于跨源数据读取类API,和资源嵌入的逻辑完全不同:
- 这类API的设计目标就是允许前端JS直接获取、自由操作响应的原始内容,因此浏览器会对其严格执行CORS校验规则,防止恶意站点私自读取用户在其他站点的隐私数据
- 哪怕请求URL、请求方法和script标签加载的完全一致,浏览器也会在收到响应后校验服务端是否返回了合法的CORS响应头
- 如果服务端没有配置匹配当前源的
Access-Control-Allow-Origin等合法CORS头,浏览器会直接拦截响应内容,禁止JS读取任何响应数据,同时抛出CORS跨域错误
常见认知误区:跨域限制不代表请求被浏览器拦截无法发送。绝大多数场景下跨域请求都会正常发送到服务端,服务端也会正常处理并返回响应,区别只是浏览器会不会把响应内容开放给前端JS代码读取。script标签场景下浏览器自行消费响应内容(执行脚本),不需要开放读取权限,因此不需要CORS许可;fetch场景下JS需要直接读取响应内容,就必须通过CORS校验。
内容的提问来源于stack exchange,提问作者Ariel
相关产品推荐
相关产品推荐

