引子
相信大家一定看到过“播放量达到 XX 万次”、“日活用户、月活用户达到 X 亿人”……这些不同维度的访问统计数据,不仅让用户觉得高大上,也对互联网平台的运营起到至关重要的数据支撑作用。
而对于网站来说,要想知道网站是否受欢迎?哪些板块、文章更受欢迎,就需要引入网站的访问统计服务。并且,国内的网站公安备案时,在网站安全评估里也要说明如何记录访客信息的。
正因如此,访问统计成为了站长们的必备工具,有了它,网站在互联网上不再是”一抹黑“。
下面,本文将介绍一款开源的网站访问统计系统—— Umami,这也是本站目前使用的方案。
为什么选 Umami
在建站的早期,我试用了几种国内外的访问统计服务,如百度统计、Google Analytics、CloudFlare Analytics等。总体感觉国内的服务提供商近几年更新比较慢,平台的UI看起来比较古老,国外的UI布局更加现代化,但是访问速度较慢,会拖慢页面的加载速度,而且它们都有一个共同的缺点──太容易被浏览器插件拦截了。
由于主流的网站访问统计服务的背后都和搜索引擎、广告联盟有关联,很难保证自己网站的统计数据不被第三方使用。因此,出于对网站访客以及站长的隐私保护,就需要选择一个可以私有化部署、数据完全可控的网站访问统计服务。
Umami 是一个开源的网站访问统计项目,作为 Google Analytics 的替代品。更加吸引我的一点是,支持灵活配置,能够突破浏览器广告拦截插件的“误伤”。

Umami 也提供 SaaS 云服务,只是还未测试过国内访问速度如何。
Umami 私有化部署
这里分享几种不同的部署方式,涵盖了我曾经走过的不同技术路线。
无论选择哪种方式,部署成功后默认的登录信息都是一样的,用户名是admin,密码是umami。
云函数服务:Vercel
Vercel 是一个在线网站托管平台,为用户提供免费的使用额度,足够部署 Umami 服务。
不过,经过一段时间的使用,我发现网站的国内访问并不稳定,并且存在函数服务冷启动的延时。如果生产环境使用请务必选择稳定的环境。
Umami 官方提供了部署文档,步骤可以总结如下:
在 GitHub 上 Fork Umami 代码仓库到自己的账号。
在 Vercel 绑定自己的 GitHub 账号,并导入自己账户下 Fork 的 Umami 代码仓库。
配置 Vercel 网站基本信息、环境变量以及数据库。
至此,就可以通过 Vercel 地址<deploy-id>.vercel.app访问 Umami 了。
为了避免本文内容过时产生误导,强烈建议参照官方文档完成部署。
本机容器部署:Docker Compose
首先,创建docker-compose.yml文件,在文件中放入以下内容。我根据官方文档做了一些修改,更加直观和简化。
注意:
pgsql 数据库密码以及APP_SECRET请改成自己的。
pgsql 数据库的挂载路径请改成自己的。
pgsql 数据库的端口无需对外开放,除非需要迁移数据而临时打开,但为了安全起见,尽可能通过正向代理等方式从外部访问。
version: '3'
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
ports:
- "30033:3000"
environment:
DATABASE_URL: postgresql://umami:umami@db:5432/umami
DATABASE_TYPE: postgresql
APP_SECRET: yoursecret
depends_on:
db:
condition: service_healthy
init: true
restart: always
healthcheck:
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
interval: 5s
timeout: 5s
retries: 5
db:
image: postgres:15-alpine
# ports:
# - "15432:5432"
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: umami
volumes:
- /path/to/umami/db:/var/lib/postgresql/data
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5
本地自动部署:1 Panel
为了方便管理,我在云主机上部署了开源面板 1 Panel,如果你也在使用 1 Panel,可以通过面板自带的 Docker Compose 应用商店部署 Umami。

部署的操作流程很直观,只是需要注意两点:一是需要先通过应用商店安装 PostgreSQL 或 MySQL 数据库;二是要设置 Umami 环境变量,这在下文突破广告拦截章节需要用到。
参考下图,通过高级设置-编辑 compose 文件来手动修改environment
部分。

不得不说,1Panel 的应用商店挺好用的,还包含了许多私有化部署的服务,比如本站使用的评论服务 Twikoo,搜索服务 MeiliSearch 等。
私有化部署的总结
选择私有化部署是为了更好的隐私保护和避免服务商锁定,然而私有化部署的环境会面临较多不确定性,例如网络攻击、主机异常等,这也给站长增加了一些运营压力。
根据这几年折腾的经验,我认为私有化部署的环境一定要有一套灵活的备份和恢复机制,方便站长做数据保护以及服务迁移。除了服务商锁定,也要注意避免被复杂的本地化部署流程和服务依赖给“私有化锁定”了。
前些年,云原生领域提出了基础设施即代码(Infrastructure as Code)技术,其中代表性的产品是 Terraform,可以用声明式的代码管理虚拟机、虚拟网络、K8S等基础设施资源,辅以 Helm Chart 可以实现应用程序的声明式代码管理,以此实现企业领域私有化部署的自动化、可复制、一致性。
感兴趣的同学可以深入了解云原生领域的技术,对技术广度提升十分有帮助。不过对于站长而言,采用 1 Panel 这类服务器管理面板就足够了。
如果你采用本地部署的方式,最后要记得为 Umami 配置反向代理哈。
Umami 的基本使用
当部署成功以后,默认的登录信息都是用户名admin,密码umami。
修改管理员信息
进入 Umami 首先要更改语言和时区设置,如下图。

其次是修改管理员用户名和密码,切记将默认的admin修改成其他的用户名,例如我将用户名设为admin-<随机字符>,具体步骤如下图所示,修改完成后重新登录系统。

添加网站
可以在设置里面添加进行访问统计的网站信息,如下图所示。

在网站代码中集成 Umami
集成 Umami 需要获取两个
跟踪代码片段:一段 HTML 代码,放在要集成访问统计的网站代码中。
跟踪脚本文件:一个 JS 文件,加载访问统计代码,可直接通过 Umami 获取或放在网站静态资源目录中。本章节采用通过 Umami 获取的方式,因此无需进行单独配置。
首先要获取 Umami 的跟踪代码片段,如下图所示。


将跟踪代码片段复制到网站 HTML 模板的末尾,这样全站所有页面都能开启访问统计。例如:
……
<!-- 下面是访问统计代码 -->
<script defer src="https://xxx.com/script.js" data-website-id="id"></script>
</body>
</html>
最后,发布新版网站。
至此,Umami 已经在你的网站生效了,可以尝试访问网站,就可以在仪表盘查看访问统计数据了。
下图是 Umami 官方效果图。

访问统计虽好,但却容易被浏览器的广告拦截插件”误杀“。那么接下来是一些优化步骤,能够理论上不再被”误杀“。
突破广告拦截
Umami 私有化部署能够有凭借其灵活性突破浏览器广告拦截插件的屏蔽,只需要以下几个步骤。
修改 JS 统计脚本文件名
广告拦截插件会收集访问统计服务的跟踪脚本文件名称,并对疑似脚本的加载进行拦截。
如果你的网站加载了一个名为analytics.js
的脚本文件,那么极有可能会被拦截。
那么应对措施也十分简单,可以通过 Umami 部署的环境变量更改跟踪脚本名称,添加TRACKER_SCRIPT_NAME
环境变量,设置为你想要的名称,例如main-logic.js
。
然后,重启 Umami 服务就会生效了。你会发现跟踪代码片段变成下面这种格式了,最后将其更新到网站代码中即可。
<script defer src="https://xxx.com/main-logic.js" data-website-id="id"></script>
当然,还有一种方式,就是将跟踪代码文件下载到本地,放到网站代码中作为静态 JS 资源引入,可以达到同样的效果。跟踪代码片段需要做对应修改,例如:
<!-- /path/to 是网站静态资源目录发布后的在线访问路径,data-host-url 是 Umami 的网址 -->
<script defer src="/path/to/main-logic.js" data-host-url=”https://stat.xxx.com“ data-website-id="id"></script>
修改服务端数据收集接口
广告拦截插件会对疑似数据收集的接口请求进行拦截。
如果你的网站请求了一个名为/api/analytics的接口服务,那么极有可能会被拦截。
这同样可以通过 Umami 部署的环境变量更改,添加COLLECT_API_ENDPOINT
环境变量,设置为你想要的接口名称,例如/api/login
。注意:本文撰写之时,Umami 规定接口格式必须为/api/xxx
。
最后,重启 Umami 服务就会生效了,并且,跟踪代码文件中也会自动更新接口地址。
如果你在上一步选择采用本地存放跟踪代码文件的方式,还需要重新下载更新的脚本文件并替换本地的老版本。
魔改监控代码,修改文件 Hash 值
虽然目前 AdGuard 没有对 JS 文件进行 hash 一致性校验以匹配拦截规则,但是保不准以后会呢?
为了以防万一,我们这里对 Umami 的统计 JS 文件进行魔改,使其文件内容发生改变,进而无法被通过内容校验拦截。
方法很简单:选择采用本地存放跟踪代码文件的方式,然后在 JS 文件的开头或结尾处,增加任意数量的空格或者;分号字符,支持混合输入。
例如:; ; ;
、;;;;
、;; ;;
。
添加完成后,技术上两个文件的代码作用是等价的,但内容的哈希校验却不同,那么就无法被识别并被拦截了。
最后,上线前记得测试一下浏览器控制台是否出现错误信息~
总结
文中介绍的 Umami 是一种客户端访问统计技术(在用户浏览器运行),为了得到全方位的统计数据,往往会辅以服务端访问统计(服务器上 Nginx 等反代服务的日志),感兴趣的同学可以通过“Nginx 日志分析”等关键词搜索了解。
客户端访问统计的优势是能够得到真实用户的访问数据,因为技术上搜索爬虫和恶意爬虫通常不会执行客户端访问统计的代码,因此不会被误记录。
但是真实的场景下,会存在客户端访问统计代码被浏览器广告拦截插件“误杀”的情况,这也是本文后半部分尝试解决的问题。理论上,参照本文进行配置,网站访问统计就不会被“误杀”了。
虽然浏览器广告拦截插件的初衷是好的,但是仍然存在拦截规则僵化的问题。本站开发阶段时,我发现页面中使用banner
作为类名的<footer>
标签就被 AdGuard 给“误杀”了,最后不得已换了其他名字。希望将来广告拦截能够更加智能吧。
最后,希望本文对各位站长有帮助,如果遇到问题,欢迎在评论区交流~
原文首发于本人博客 blog.jiaxiang.wang 及同名公众号"王嘉祥",如果文章内容已经过时,请访问原文查看更新。