一、 核心背景:为什么要折腾?
在开发面向国内用户的 Web 项目(如我的图床 PicKoala)时,Google Analytics (GA4) 和 Google Tag Manager (GTM) 几乎是处于“不可用”状态的。
根本原因:
-
网络屏蔽:由于众所周知的原因,用户端无法直接建立与 Google 统计服务器的连接。
-
插件拦截:即便网络通畅,主流的广告拦截插件(AdBlockers)也会根据域名黑名单直接掐断发送往
google-analytics.com的数据包。
为了让数据能回来,我们需要一个“中间人”——也就是部署在 Cloudflare 边缘节点上的 Worker。
二、 踩坑历程:从“逻辑死循环”到“降维打击”
在调试过程中,我们遇到了一个极其隐蔽的技术坑:
-
SSL 证书限制:Cloudflare 免费版证书只支持到二级域名(如
*.example.com)。如果我们使用www.metrics.example.com这种三级嵌套域名,会直接导致ERR_CONNECTION_CLOSED。 -
域名叠加报错 (www.www):Google 的脚本内部逻辑非常固执,它会自动给域名补全
www.。如果我们把google-analytics.com替换为www.yourdomain.com,最终浏览器发出的请求会变成www.www.yourdomain.com。这是一个合法的四级域名,但 Cloudflare 并不认识它,连接依然会重置。
最终方案:降维打击 我们放弃了使用子域名作为代理地址,转而只绑定根域名。利用 Google “爱加 www”的特性,让它帮我们生成合法的二级域名。
三、 核心代码实现
1. Cloudflare Worker 脚本
这份脚本负责接收请求、转发给 Google,并在返回给用户前完成域名的动态修正。
// 核心配置:只绑定根域名
const PROXY_DOMAIN = 'pandax.mom';
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const pathname = url.pathname;
const search = url.search;
// 1. 路由映射:定义需要代理的资源路径
let targetUrl = "";
if (pathname.startsWith("/gtm.js")) {
targetUrl = `https://www.googletagmanager.com/gtm.js${search}`;
} else if (pathname.startsWith("/gtag/js")) {
targetUrl = `https://www.googletagmanager.com/gtag/js${search}`;
} else if (pathname.startsWith("/ns.html")) {
targetUrl = `https://www.googletagmanager.com/ns.html${search}`;
} else if (pathname.includes("/collect")) {
targetUrl = `https://www.google-analytics.com${pathname}${search}`;
}
if (!targetUrl) return new Response("Not Found", { status: 404 });
// 2. 转发请求,保持 Headers 原始性
const newRequest = new Request(targetUrl, {
method: request.method,
headers: request.headers,
body: request.body,
redirect: 'follow'
});
try {
const response = await fetch(newRequest);
const contentType = response.headers.get("content-type") || "";
// 3. 深度文本替换逻辑
if (contentType.includes("javascript") || contentType.includes("text")) {
let text = await response.text();
// 关键:替换域名主体。
// 原始 [www.google-analytics.com](https://www.google-analytics.com) 会自然演变为 www.pandax.mom
const modifiedText = text
.replace(/googletagmanager\.com/g, PROXY_DOMAIN)
.replace(/google-analytics\.com/g, PROXY_DOMAIN)
// 兜底修正:彻底杀掉 www.www 的可能性
.replace(/www\.www\.pandax\.mom/g, 'www.' + PROXY_DOMAIN);
const resHeaders = new Headers(response.headers);
resHeaders.set("Access-Control-Allow-Origin", "*");
resHeaders.delete("content-security-policy");
resHeaders.delete("x-frame-options");
return new Response(modifiedText, { status: response.status, headers: resHeaders });
}
return new Response(response.body, {
status: response.status,
headers: { "Access-Control-Allow-Origin": "*" }
});
} catch (e) {
return new Response("Proxy Error", { status: 502 });
}
}
};
2. 前端 HTML 配置
修改 GTM 加载地址,直接请求 Worker 绑定的根域名。
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;
// 指向根域名,GA4 后续会自动拼接 www.pandax.mom 进行数据上报
j.src='[https://pandax.mom/gtm.js?id='+i+dl](https://pandax.mom/gtm.js?id='+i+dl);
f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-K4CFSKBC');</script>
四、 避坑指南:Umami 的 422 报错
在同时使用 Umami 统计时,可能会遇到 API 返回 422 Unprocessable Content。
解决方案:
-
ID 校验:确保 GTM 或 HTML 里的
data-website-id是真实的 UUID,而不是占位符。 -
清空域名白名单:在 Umami 后台设置中,将 Domain (域名) 选项彻底清空。因为上报数据经过了 Worker 转发或 Origin 发生了变化,清空白名单可以确保统计服务接收任何来源的合法数据。
五、 总结
通过这套方案,我们实现了:
-
100% 统计送达:不再受限于用户本地的网络环境。
-
隐身运行:代理域名不含
google字样,绕过了 90% 以上的广告拦截插件。 -
零额外开销:完全运行在 Cloudflare Worker 的免费额度内。
如果你的项目也面临统计数据“人间蒸发”的问题,不妨试试这招“降维打击”。



评论(0)