搭建代理防止 Google Analytics 代码被客户端屏蔽

每课(我创立的开源项目)前端一直使用的是 CNZZ 的网站统计服务(现在叫友盟了),前段时间因为做表单验证的需要开始使用谷歌的 ReCaptcha,突然想到谷歌的 Analytics 是不是也可以在国内正常使用呢?毕竟广告是谷歌的发动机,在网站统计、用户画像这方面谷歌应该还是非常专业的。于是决定开始尝试使用 Google Analytics。

搭建代理防止 Google Analytics 代码被客户端屏蔽

每课(我创立的开源项目)前端一直使用的是 CNZZ 的网站统计服务(现在叫友盟了),前段时间因为做表单验证的需要开始使用谷歌的 ReCaptcha,突然想到谷歌的 Analytics 是不是也可以在国内正常使用呢?毕竟广告是谷歌的发动机,在网站统计、用户画像这方面谷歌应该还是非常专业的。于是决定开始尝试使用 Google Analytics。

一切都非常顺利,直到我发现我在实时面板中看不到自己的访问。打开 Chrome 调试工具才发现原来 Google Analytics 的 JS 文件被我的网络优化服务商提供的分流规则自带的“防大数据”规则给屏蔽了。既然我遇到了这个问题,我的用户也可能出现类似的情况(比如统计代码被浏览器的广告拦截插件屏蔽)。

不准确的数据就是没有意义的数据。

本着这个原则,我决定着手解决这个问题。

上谷歌搜了一下,其他人也遇到了这个问题,解决的方式是使用代理来转发请求。Medium 上的一篇文章作者使用 NodeJS 来搭建简易的转发工具。粗略的看了一下代码,写的非常简单,不知道在大流量下会不会遇到性能问题。我自己还是决定使用 Nginx 来做反向代理。


在造轮子之前先看一下有没有别人已经做好了的东西。于是我去 Docker Hub 搜索 google analytics proxy,还真找到了一个结果。顺着链接找到了 GitHub 的仓库,并 fork 了下来。发现没有 README,好在整个仓库只有两个文件:一个 Nginx 的 conf 文件,一个 Dockerfile。在把两个文件都读了一遍之后我发现了两个问题:

  • 这个代理只支持突破客户端对于 analytics.js 的限制,不支持 gtag.js (新版 analytics.js)和 gtm.js(Google Tag Manager)
  • 客户端的真实 IP 可能不能正确体现在 GA 的地图中,因为没有看见相关的代码

第一个问题好解决,只要将相应的文件添加到 Nginx 的配置文件中即可。第二个问题则稍嫌麻烦。事实上,在我没有修改他的代码之前,实测出现了所有客户端的位置变成了代理服务器所在位置的情况。

通过之前提到的那篇文章的提醒,Google Analytics Measurement Protocol URL 参数中有一项可以指定用户的 IP。因此我们只需在 Nginx 中获取客户端的真实 IP 并添加到 URL Query String 中即可。

    location /a/ {
        set $delimiter "";
        if ($is_args) {
            set $delimiter "&";
        }
        if ($http_x_real_ip = "") {
            set $http_x_real_ip $remote_addr;
        }
        set $args "${args}${delimiter}uip=${http_x_real_ip}";

        proxy_pass http://www.google-analytics.com/;
    }

以上是项目中截取的代码。当请求头中存在 X-Real-IP 时,使用其提供的内容作为 IP 地址(由最外层反向代理添加);如果没有这个 Header 则表明当前已经是最外层 Nginx,这时使用 $remote_addr 来获取 socket 对端的地址。


在解决了这些问题之后,我发布了最终的代码,你可以在 https://github.com/fr0der1c/google-analytics-proxy 查看。此外,镜像也已经发布在 Docker Hub 上,你可以直接使用 fr0der1c/google-analytics-proxy ,无需自己构建。

后记

经过和原作者的沟通,他使用了 Cloudflare 的 API 来获得客户端 IP ,并且这个信息在发布出来了的代码中省略了,因此他没有出现所有客户端都是一个 IP 的情况,而你使用他的代码会发生这种情况。不过,现在你只需要使用我的镜像、并替换页面上的 Google Analytics JS 文件地址即可,也无需担心这种情况的发生。