Cloudflare Workers和vercel worker实现图片反向代理实现html2canvas截图跨域图片

在开发纯前端的番剧九宫格工具时,我遇到了一个经典问题:html2canvas 无法截取来自第三方 API(如 AniList、Bangumi)的图片。原因是这些图片服务器没有设置 Access-Control-Allow-Origin 头,浏览器出于安全策略限制了 canvas 对跨域图片的读取。

解决方案是用反向代理——让图片请求先经过自己的服务器,在响应中手动添加 CORS 头。这里使用了两种方式:自建 Cloudflare Worker 和 朋友提供了 vercel worker 来做反向代理。两者可以配合使用,提高成功率。

原理简述

  1. 前端不直接请求图片源站,而是请求代理服务(格式如 https://代理地址/proxy?url=图片原始URL)。
  2. 代理服务收到请求后,用后端(Worker)去获取图片,同时可以自定义请求头(如 User-Agent、Referer)以绕过防盗链。
  3. 代理服务将图片返回给前端,并在响应头中强制添加 Access-Control-Allow-Origin: *
  4. 此时对浏览器来说,图片来源于同源的代理地址,跨域限制消失,html2canvas 就能正常绘制。

Cloudflare Workers

1. 创建 Worker

  • 登录 Cloudflare Dashboard,进入 Workers & Pages
  • 点击 创建应用程序Create application 选择Start with Hello World! ,或或 “+Add“ → 创建 Workers
  • 命名(例如 anime-image-proxy
  • 部署后点击 编辑代码,将以下 JavaScript 代码粘贴进去:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
export default {
async fetch(request) {
const url = new URL(request.url);

// --- 1. 测试请求---
if (url.searchParams.has("test")) {
return new Response('<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"><rect width="200" height="200" fill="green"/><text x="20" y="110" fill="white" font-size="20">Worker OK</text></svg>', {
headers: { "Content-Type": "image/svg+xml", "Access-Control-Allow-Origin": "*" }
});
}

// --- 2. 获取目标图片地址 ---
const imageUrl = url.searchParams.get("url");
if (!imageUrl) {
return new Response("Missing 'url' parameter", { status: 400 });
}

try {
// --- 3.请求头 ---
// 模拟 Chrome 浏览器的完整请求头,降低被源站拒绝的概率
const response = await fetch(imageUrl, {
headers: {
// 伪造一个常见的 User-Agent
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
// 设置一个合理的 Referer,对于 Bangumi 图片,可以设为其主站
"Referer": "https://bangumi.tv/",
// 模拟浏览器接收的格式
"Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
// 告诉源站我们支持压缩
"Accept-Encoding": "gzip, deflate, br",
// 保持连接活跃
"Connection": "keep-alive",
// 有些源站会检查这个头
"Sec-Fetch-Dest": "image",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "cross-site"
}
});

// --- 4. 处理源站返回的错误 ---
if (!response.ok) {
// 如果源站返回404或403,我们也可以返回一个友好的占位图
return new Response(`图片获取失败 (源站返回 ${response.status})`, {
status: response.status,
headers: { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" }
});
}

// --- 5. 检查内容类型 ---
const contentType = response.headers.get("content-type") || "";
if (!contentType.startsWith("image/")) {
return new Response("源站返回的内容不是图片", { status: 400 });
}

// --- 6. 构建成功响应,添加CORS头并转发图片 ---
const modifiedResponse = new Response(response.body, response);
modifiedResponse.headers.set("Access-Control-Allow-Origin", "*");
// 可以缓存图片,减少Worker请求次数
modifiedResponse.headers.set("Cache-Control", "public, max-age=86400");

return modifiedResponse;

} catch (error) {
// --- 7. 记录详细错误并返回友好信息 ---
console.error(`代理图片失败: ${imageUrl}`, error.message);
return new Response(`代理失败: ${error.message}`, {
status: 500,
headers: { "Access-Control-Allow-Origin": "*" }
});
}
}
}

2. 部署与使用

  • 部署后获得 Worker 域名:https://anime-image-proxy.你的用户名.workers.dev

  • 在前端中构造代理 URL:

    1
    const proxyUrl = `https://anime-image-proxy.你的用户名.workers.dev/proxy?url=${encodeURIComponent(原始图片URL)}`;
  • <img>src 或 CSS background-image 设为此 URL。


vercel worker


注意事项

  1. URL 编码:原始图片 URL 必须经过 encodeURIComponent,否则参数可能被截断。
  2. 防盗链:部分图片源检查 Referer,可在代理请求中伪造合适的 Referer(如 https://anilist.cohttps://bangumi.tv)。
  3. 缓存:在代理响应中加入 Cache-Control 头可以减轻代理服务器压力。
  4. 隐私:使用公共代理时,图片请求会经过第三方服务器,敏感数据请谨慎。

总结

通过 Cloudflare Workers 和 vercel worker,我们低成本地解决了前端图片跨域问题,让 html2canvas 能够顺利截图来自任何源的图片。Worker 提供完全自控的代理逻辑。

应用项目

Anime-pick-grid

项目简介

你好请吃我的番剧安利-番剧收集器是一个基于网页的番剧推荐表生成工具,用户可以通过搜索和选择番剧,创建自己的番剧推荐九宫格或更多格子的推荐表。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

You Found Me.

支付宝
微信