Vue 项目中图片跨域问题排查与解决

问题背景

最近在开发一个 Vue 项目时,遇到一个诡异的问题:页面上通过 <img> 标签加载的图片,在浏览器缓存之后,通过 JavaScript 的 fetch 方法再次获取时,会直接爆跨域错误。

这个问题很奇怪——图片明明已经在本地缓存了,为什么还会跨域报错?

问题复现

// 页面上的图片
<img src="https://cdn.example.com/image.jpg" id="myImage" />

// 通过 JS 获取图片
async function getImage() {
  const img = document.getElementById('myImage');
  const response = await fetch(img.src); // 这里爆跨域错误
  return response.blob();
}

错误信息类似:

Access to fetch at 'https://cdn.example.com/image.jpg' from origin 'https://your-site.com' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

问题根因

要理解这个问题,首先要搞清楚 crossorigin 属性的默认值。

很多人以为 crossorigin="anonymous" 是默认值,其实是错的。

真正的情况是:

属性 含义
不写 crossorigin 属性 不携带跨域凭证,不带 Origin 请求头
crossorigin="anonymous" 携带跨域凭证,发送 Origin 请求头
crossorigin=""(空字符串) 携带跨域凭证,发送 Origin 请求头
crossorigin(只有属性名,无值) 携带跨域凭证,发送 Origin 请求头
crossorigin="use-credentials" 携带跨域凭证 + Cookie,发送 Origin 请求头

只要写了 crossorigin 属性(无论值是什么),img 就会携带 Origin 请求头。 不写这个属性,才不会携带。

问题的触发需要同时满足两个条件:

  1. <img> 标签没有 crossorigin 属性:img 不带 Origin 请求头
  2. 服务器设置了图片缓存(Cache-Control):图片被浏览器缓存

两者缺一不可:

  • 没有缓存:fetch 直接发新请求,服务器正常处理(或直接报错,错误信息清晰)
  • 有 crossorigin 属性:请求带 Origin,服务器返回 CORS 头,缓存里包含跨域信息,fetch 正常
  • 没有 crossorigin 属性 + 有缓存:img 加载时没带 Origin → 服务器没返回 CORS 头 → 图片被缓存时没有跨域信息 → fetch 读取缓存 → 跨域检查失败

简单来说:缓存把没有 CORS 头的信息也一起存下来了,后续 fetch 访问时,浏览器发现响应里缺少 Access-Control-Allow-Origin,于是判定跨域失败

解决方案

<img> 标签加上 crossorigin="anonymous" 属性:

<img 
  src="https://cdn.example.com/image.jpg" 
  id="myImage" 
  crossorigin="anonymous" 
/>

加上这个属性后:

  1. <img> 发起的 HTTP 请求会带上 Origin: https://your-site.com 请求头
  2. CDN 服务器检测到 Origin 请求头,返回响应时会在响应头加上 Access-Control-Allow-Origin: *(或对应的域名)
  3. 图片连同正确的跨域响应头一起被缓存
  4. 后续 fetch() 访问时,浏览器检查缓存响应的 CORS 头,通过验证

完整示例

// 页面 HTML
<img 
  src="https://cdn.example.com/image.jpg" 
  id="myImage" 
  crossorigin="anonymous"
/>

// JS 代码
async function getImage() {
  const img = document.getElementById('myImage');
  const response = await fetch(img.src);
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  const blob = await response.blob();
  return URL.createObjectURL(blob);
}

注意事项

  1. 服务器端需要支持 CORS:CDN 或图片服务器必须返回 Access-Control-Allow-Origin 响应头,否则即使用了 crossorigin 属性也没用。

  2. 缓存需要清除:解决问题后,需要清除浏览器缓存,否则旧的没有跨域头的缓存还在,问题依然存在。

  3. 第三方图片:如果图片托管在第三方平台(如七牛、阿里云 OSS、Cloudflare 等),确保它们已正确配置 CORS 响应头。

总结

这个问题本质上是 HTTP 缓存机制与 CORS 机制的交互<img> 标签默认不带跨域信息,导致缓存的图片缺少 CORS 响应头,而 fetch API 需要这些头。

值得注意的是,这个问题在网络上几乎找不到相关讨论。 大部分 CORS 相关文章都是围绕 API 请求或字体文件展开,涉及到 <img> 缓存与 fetch 跨域交互的场景非常罕见。这也让问题的排查变得困难——报错信息指向跨域,但图片明明已经成功加载了,容易让人摸不着头脑。

解决方案很简单:<img> 标签上加 crossorigin="anonymous"。让浏览器在请求时带上 Origin 头,服务器返回对应的 CORS 响应头,缓存里就有了跨域信息,后续 fetch 就能正常访问了。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐