网站配置 Google_Analytics 的常见方式,是在网站前端引用 Google_Analytics 的 tracker_code 和 analytics.js 用以统计用户行为。而这种方式会成为访问缓慢的原因:访客对 analytics.js 的下载请求会拖慢页面加载完毕,并需要向 Google 发送访客数据,用户行为的统计提交全部在客户端由客户完成,加重了客户端的负担。并且也有 analytics.js 会被 ublock 等屏蔽掉而导致无法收集用户行为的弊端。
如果将 Google_Analytics 的统计工作从前端转移到后端完成,就可以有效避免上述问题。
在 2016 年时就已经出现了这种方式(via),该文章使用 ngx_http_userid_module 实现 userid 的生成。而基于本文所述,使用 Openresty 的好处在于 id 的生成方案由网站所有者决定,且能够享受 lua code 带来的便(kuai)利(gan)。
基本思路
通过 Nginx proxy_pass 就能实现该目的,根据用户访问的 location,按照 Measurement Protocol 向 https://www.google-analytics.com/ 提交数据,就能将统计工作放在后端完成:
location @google_analytics { proxy_pass https://www.google-analytics.com/collect?v=1&t=pageview&tid=UA-111111111-1&cid=$cookie_cid&uip=$remote_addr&dh=$host&dp=$uri&dr=$http_referer&z=$msec; }
配置 Nginx
在上面已提出了基本思路,完整的 Nginx 实现应该是这样的框架:
location / { ... post_action @google_analytics; } location @google_analytics { internal; resolver 8.8.8.8 [2001:4860:4860::8888]; proxy_http_version 1.1; proxy_method GET; proxy_set_header User-Agent $http_user_agent; proxy_pass_request_headers off; proxy_pass_request_body off; proxy_pass https://www.google-analytics.com/collect?v=1&t=pageview&tid=UA-111111111-1&cid=$cookie_cid&uip=$remote_addr&dh=$host&dp=$uri&dr=$http_referer&z=$msec; }
这个时候,以上配置中有两个需要注意的地方:
- tid=UA-111111111-1:自行修改为 Google_Analytics 分配给你网站的
- cid=$cookie_cid:这个是用户标识,按照标准应该为 uuid 格式,为每位用户分配的标识应该独一无二。
其中,这里的变量 $cookie_cid 通过 Openresty 实现,是作为分配给每次访问的客户 id,生成方式会在下文进行说明。
配置 Openresty
要通过 Openresty 生成 uuid 分配给访客,可以通过 $remote_addr 生成,这是基于 client ip 生成的客户标识:
local str_random = ngx.var.binary_remote_addr local str_md5 = ngx.md5(str_random) local str_sub = string.sub(str_md5, 1, 8) .. "-" .. string.sub(str_md5, 9, 12) .. "-" .. string.sub(str_md5, 13, 16) .. "-" .. string.sub(str_md5, 17, 20) .. "-" .. string.sub(str_md5, 21, 32) cid = str_sub
或者也可以通过访问时间生成,将 str_random 换一种定义方式,其中 ngx.time() 函数提供秒级的当前 unix 时间戳:
ngx.update_time() local str_random = ngx.time()
然后需要做的就是把 Openresty 中的这个变量 $cid 传递到 Nginx,即在 location 中所需要的 $cookie_cid 变量。使用 cookie 传递是最简单的方法:
local cookie_cid = nil if (ngx.var.cookie_cid == nil) then cid = userid.generate() end ngx.header["Set-Cookie"] = {"cid=" .. cookie_cid}
然后 Nginx 中 proxy_pass 时就会读取 cookie 中的 cid 变量(即 $cookie_cid 变量),以此作为访客标识发送至 Google_Analytics 服务。
不发送的行为
“不发送的行为”包括并不限于 spider 和 request_file_not_exist 两种常见情况,爬虫和 404 我们是不会想提交给 Google_Analytics 服务的。可以加入是否执行 post_action 的判断。
通过 Openresty 判断筛选不希望行为统计被提交的情况:
-- 这里把 ".../" 替换成你的网站文件的绝对路径 local request_filename = ".../" .. ngx.var.uri -- 判断请求文件是否存在 local file = io.open(request_filename, "r") if (file and ngx.re.find(ngx.var.http_user_agent, "qihoobot|Baidu", "jo") == nil) then ngx.header["Set-Cookie"] = {"stats=" .. "1"} end
然后在 Nginx 中进行判断,仅满足 $cookie_stats = 1 的访问提交给统计服务:
location / { ... if ($cookie_stats = "1") { post_action @google_analytics; } }
Lib 实现
本文仅提供最基本的思路介绍,完整的实现方案我已封装为 lib 并发布于 Github。
本站已使用本文所述方案,效果可参见本站 cookie。
Reference
感谢以下作者提供的思路:
- https://darknode.in/network/nginx-google-analytics/
- https://eason-yang.com/2016/11/04/google-analytics-via-nginx/
- https://blog.nfz.moe/archives/google-analytics-optimize.html