代理 Google Analytics

在使用 Google Analytics 的时候,只需加上这么一段 js 就能开始统计数据了:

<script async src="https://www.googletagmanager.com/gtag/js?id=<YOUR_GAID>"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', '<YOUR_GAID>');
</script>

但这会带来如下几个问题:

  • 提供的网址在大陆地区不稳定
  • 容易被广告插件拦截从而影响准确性

之前我使用 配合 Measurement Protocol 优化 Google Analytics 的方式来解决以上问题,同时支持了 SPA 也能自由控制功能与开销之前的取舍,就是麻烦,不能开箱即用,还有后期维护问题。

后来,在查看文档的时候,发现官方已经升级到了 v4 的版本 ,即 Google Analytics v4 + Measurement Protocol v2 的组合,并且默认就能自动追踪 SPA 的路由变化,不像之前需要额外引入插件使用,真的能开箱即用了。剩下就是怎么解决上面这些问题了,思路就是使用自定义的地址来替换并代理官方地址,目前我找到这些方案:

  • Google Tag Manager 支持在自己的服务器上搭建统计服务。缺点在于只能使用 Google Cloud Platform 的服务器
  • save-analytics-from-content-blockers 劫持 gtag 的 js 文件,修改其中用于收集数据的 API 地址,并对这些地址做混淆。使用的时候,只需修改官方 js 代码中 gtag 的引用路径即可。

第二种很好用很全面,就是这个得用容器跑,而我只是想简单的使用统计服务就可以了,于是稍微整合下两种方法:

在第一种方法中,文档说到,可以使用参数 transport_url 来定义 "收集API" 的域名,解决了稳定性的问题,但仍然会被拦截,因为这个参数无法修改默认路径(/g/collect),所以像方法二那样劫持 gtag,替换默认的 /g/collect 为其他就可以了。

  • Vercel Function :
const Fastify = require("fastify");
const reply = require("fastify-reply-from");
const axios = require("axios").default;

const app = Fastify();

app.register(reply);

app.get("/gtag-js", async (request, reply) => {
  const { query } = request;
  const response = await axios.get("https://www.googletagmanager.com/gtag/js", {
    params: query,
  });

  reply.headers({
    "content-type": "application/javascript; charset=UTF-8",
  });
  reply.send(response.data.replace(/"\/g\/collect"/g, `"/g-collect"`));
});

app.all("/g-collect", function (request, reply) {
  reply.from(`https://www.google-analytics.com/g/collect`);
});

module.exports = async function (req, res) {
  await app.ready();
  app.server.emit("request", req, res);
};
  • Client JS:
<script async src="https://<YOUT_DOMAIN>/gtag-js?id=<YOUR_GAID>"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', '<YOUR_GAID>', {
      transport_url: 'https://<YOUT_DOMAIN>'
  });
</script>

使用 Measurement Protocol v2 的情况下不能像 v1 那样主动指定访客的地理位置。