在Nodeseek中了一份09cdn的亚太地区的CDN加速套餐,于是想折腾一下,具体思路是:
- 大陆地区应用09cdn加速
- 其他地区依然使用Cloudflare的大善人解析
思路确定后,便考虑使用Cloudflare Worker实现分区域CDN加速,具体折腾过程如下:
第1步:配置目标CDN
在09cdn控制台中,添加需要加速的域名 bb.bins.fyi,完成源站配置后得到一个CNAME记录值,比如bb.09cdn.com.
第2步:Cloudflare DNS的关键设置(两步)
这是整个方案的关键,包含两条重要的DNS记录:
-
主域名A记录(橙色云朵):
- 类型:
A - 名称:
bb - 地址:
指向真实的源站服务器IP地址 - 代理状态: 必须是“已代理”(橙色云朵)。
- 说明:这条记录是整个分流方案的入口。开启代理后,所有对
bb的请求都会先被导向Cloudflare的边缘网络,从而触发Worker。这里的IP地址是Worker在处理非中国大陆流量,或Worker执行失败时的最终回源地址,因此必须是真实的源站IP。
- 类型:
-
代理CNAME记录(灰色云朵):
- 类型:
CNAME - 名称:
cdn(这个子域名可自取,作用是作为区内代理) - 目标:
bb.09cdn.com(指向在第1步中获取的CNAME) - 代理状态: 必须是“仅限DNS”(灰色云朵)。
- 类型:
第3步:编写并部署Cloudflare Worker脚本
这是实现智能分流的核心:
/**
* Cloudflare Worker for Geo-based CDN Routing
* - FINAL VERSION -
* - Correctly modifies the RESPONSE headers returned to the browser.
* - Uses an in-zone CNAME for resolveOverride to satisfy security policy.
* - Includes try...catch for robustness.
*/
// v5 - Production Ready
addEventListener('fetch', event => {
event.respondWith(handleRequest(event))
})
async function handleRequest(event) {
try {
const request = event.request;
// !! 指向您在Cloudflare DNS中创建的灰色云朵CNAME
const chinaCdnHost = 'bb.09cdn.com';
const mainlandChinaCountryCode = ['CN'];
// 从Cloudflare请求头中获取国家代码
const country = request.headers.get('cf-ipcountry');
let response;
if (country && mainlandChinaCountryCode.includes(country)) {
// 1. 发起子请求到上游服务 (目标CDN)
const upstreamResponse = await fetch(request, {
cf: {
resolveOverride: chinaCdnHost
}
});
// 2. 基于上游响应创建一个可修改的响应副本
response = new Response(upstreamResponse.body, upstreamResponse);
// 3. 在这个返回给浏览器的新响应上添加标记
response.headers.set('X-Worker-Routed-By', 'China-CDN-Router');
} else {
// 对于非中国大陆地区,流程相同
const upstreamResponse = await fetch(request);
response = new Response(upstreamResponse.body, upstreamResponse);
response.headers.set('X-Worker-Routed-By', 'Global-Default');
}
// 4. 返回最终修改过的响应给浏览器
return response;
} catch (error) {
console.error('Worker Error:', error);
// 如果发生任何错误,返回一个明确的错误信息
return new Response(`Worker script failed: ${error.message}`, {
status: 500,
headers: { 'X-Worker-Error': 'True' }
});
}
}
第4步:绑定路由并验证
- 绑定路由:在Cloudflare仪表板中,为Worker添加一条路由规则,将
bb.bins.fyi/*指向刚刚创建的Worker。 - 验证:使用全球节点测试工具(如17CE、boce.com),访问
bb.bins.fyi,并检查HTTP响应头。- 中国大陆节点应返回
x-worker-routed-by: China-CDN-Router。 - 海外节点应返回
x-worker-routed-by: Global-Default。
- 中国大陆节点应返回
调试过程中遇到一些坑点
坑点1:Error 526与resolveOverride的安全陷阱
- 现象:在脚本中直接将
resolveOverride指向09cdn的外部CNAME(bb.09cdn.com),中国大陆地区的访问直接返回“Error 526: Invalid SSL certificate”。 - 原因:这是Cloudflare的一个重要安全机制。
resolveOverride只在请求主机和目标主机同属于一个Cloudflare区域(Zone)时才生效。我们的请求主机是bb.bins.fyi,而目标是外部域名,Cloudflare出于安全考虑会静默地忽略该指令,导致SSL握手时因SNI不匹配而失败。 - 解决方案:这正是第2步中创建“灰色云朵”CNAME的用意。我们创建了一个属于自己Zone的别名
cdn.bins.fyi,它指向外部CDN。在Worker脚本中,resolveOverride指向这个区内别名,安全检查通过,问题解决。
坑点2:Worker脚本的“静默崩溃”
- 现象:有时能看到09cdn有零星的请求记录,但测试时却总是访问到源站,没有任何错误提示。
- 原因:当Worker脚本在执行中遇到未捕获的异常(如网络超时)时,Cloudflare会采取“故障安全”模式,即静默地放弃Worker执行,直接将请求代理到源站,以保证服务的可用性。这给调试带来了巨大的困难。
- 解决方案:为整个Worker脚本包裹上一个
try...catch块。在catch中,我们不再让它静默失败,而是主动返回一个包含错误信息的HTTP 500响应。这使得任何潜在的运行时错误都能被立即发现。