一、 核心背景:为什么要折腾?

在开发面向国内用户的 Web 项目(如我的图床 PicKoala)时,Google Analytics (GA4) 和 Google Tag Manager (GTM) 几乎是处于“不可用”状态的。

根本原因:

  • 网络屏蔽:由于众所周知的原因,用户端无法直接建立与 Google 统计服务器的连接。

  • 插件拦截:即便网络通畅,主流的广告拦截插件(AdBlockers)也会根据域名黑名单直接掐断发送往 google-analytics.com 的数据包。

为了让数据能回来,我们需要一个“中间人”——也就是部署在 Cloudflare 边缘节点上的 Worker。

二、 踩坑历程:从“逻辑死循环”到“降维打击”

在调试过程中,我们遇到了一个极其隐蔽的技术坑:

  1. SSL 证书限制:Cloudflare 免费版证书只支持到二级域名(如 *.example.com)。如果我们使用 www.metrics.example.com 这种三级嵌套域名,会直接导致 ERR_CONNECTION_CLOSED

  2. 域名叠加报错 (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

解决方案:

  1. ID 校验:确保 GTM 或 HTML 里的 data-website-id 是真实的 UUID,而不是占位符。

  2. 清空域名白名单:在 Umami 后台设置中,将 Domain (域名) 选项彻底清空。因为上报数据经过了 Worker 转发或 Origin 发生了变化,清空白名单可以确保统计服务接收任何来源的合法数据。

五、 总结

通过这套方案,我们实现了:

  1. 100% 统计送达:不再受限于用户本地的网络环境。

  2. 隐身运行:代理域名不含 google 字样,绕过了 90% 以上的广告拦截插件。

  3. 零额外开销:完全运行在 Cloudflare Worker 的免费额度内。

如果你的项目也面临统计数据“人间蒸发”的问题,不妨试试这招“降维打击”。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。