如果让我用一个词解释网站分析,我会选 hindsight。字面意思是“事后才看明白”,中文里最贴近的说法是“事后洞察、复盘”;放到网站场景里,它就是一面后视镜——用户来过、看过、离开,这些事只有事后才留下数据。Hindsight 恰好也是一套开源的、可自托管的网站分析平台的名字,目标就是把这面“后视镜”装回你自己的服务器上。
这趟迁移里,我把个人博客的统计从 Google Analytics 换成了 Hindsight:去掉了 Cookie 弹窗,去掉了第三方脚本,数据全在自己机器里,查询速度还快得离谱。这篇文章不打算复述官方文档,只记录我实际部署、接入、看数、排错的全过程,以及它和 Plausible、Umami 这类工具之间的取舍。想彻底掌控访问数据的人、被 Cookie 弹窗烦到不行的站长,以及刚开始接触自托管分析产品的开发者,都适合接着往下看。
1. Hindsight 的整体设计与思路拆解
1.1 为什么我会倾向自建分析平台
先说动机。我一直觉得 Google Analytics 功能确实强,但放在今天这个小网站上有点“杀鸡用牛刀”。它默认会种 Cookie,访客第一次进来就得面对一串授权弹窗;数据虽然免费,但采样、延迟、数据出口这些事让我心里没底。最要命的是,我导出原始事件数据时,发现很多分析需求在免费额度下根本绕不过“抽样”这两个字。
自建分析平台解决的就是这件事:数据进你的服务器,出你的数据库,规则你自己定。Hindsight、Plausible、Umami、Matomo 都是这条路。它们通常不依赖 Cookie,不给访客加跟踪指纹,法律合规压力小很多,而且部署完以后,代码和数据库都捏在自己手里。
我的博客访问量不算大,一天几千 PV,一台 2C2G 的云服务器完全跑得动。换下来之后最直观的感受是:页面不再额外加载一堆第三方域名资源,隐私弹窗消失,分析面板打开速度比 GA 快一个量级。如果说在给别人做产品时还得考虑团队协作和功能完整性,那自己的小网站、小工具箱,自托管是性价比最高的方案。
1.2 技术栈为什么是 Go + ClickHouse
Hindsight 后端用 Go 写,前端是 React,数据层放在 ClickHouse 里。这个组合第一眼有点重,但拆开看非常合理。
Go 的好处是部署简单:编译完就是一个二进制,跑起来没有 JVM 那一大套依赖。就算用 Docker,镜像也小,内存占用比 Java 系的产品友好太多。React 负责看板层,图表交互和筛选器体验做得很顺。
真正的重点是 ClickHouse。它是个列式数据库,和 MySQL、PostgreSQL 这种行式数据库的思路完全不一样。行式库适合频繁增删改一行数据,而分析场景的查询往往是“扫描几百万行,但只取其中两列做聚合”。列式库把每列单独存储,查询时只需要读相关列,所以“今天有多少访客”“哪个页面最受欢迎”这类统计能秒级出结果。
用生活化的类比解释:行式数据库像一本按日期写满的日记,你想统计这个月提到“爬山”的次数,得把每一页从头翻一遍;列式数据库像一沓按主题整理的卡片,你只需要抽出“活动”那一沓数一遍。事件分析天生适合这种结构。
1.3 事件模型是这套工具的精髓
Hindsight 没有把“页面浏览量”当成唯一的数据类型,而是把所有行为都抽象成事件。一条事件记录通常包含这些字段:站点标识、事件名、时间戳、当前 URL、来源 URL、会话 ID、用户代理,以及可选的业务属性。
{ "site_id": "blog", "event": "pageview", "ts": 1735689600, "url": "https://example.com/posts/hindsight-deploy", "referrer": "https://duckduckgo.com/", "session_id": "8f4a91c2e6", "meta": { "title": "Hindsight 自托管部署" } }这个设计的妙处在于:先存原始事件,展示层再按需求聚合。今天想看 PV,就把事件按 URL 分组;明天想算转化率,就把“注册按钮点击”和“注册成功”两个事件串起来。它不像传统统计工具那样预先把几十个报表字段算好,而是把灵活性留到了查询阶段。
我是做产品出身,特别吃这一套。因为埋点需求永远会变,今天要追踪下载按钮,明天要追踪搜索框,如果底层模型不支持自定义事件,后面每个新指标都要改数据库结构,太痛了。事件模型等于提前把口子开好,你只需要往里塞数据。
1.4 和 Plausible、Umami 等同类工具的横向对比
自托管分析工具已经不少,选 Hindsight 之前我也对比过几个:
| 工具 | 技术栈 | 存储 | 隐私特点 | 部署难度 | 核心优势 |
|---|---|---|---|---|---|
| Plausible | Elixir + ClickHouse | ClickHouse 或 Postgres | 无 Cookie,IP 匿名 | 中 | 成熟稳定,功能打磨细 |
| Umami | Next.js + Prisma | Postgres | 无 Cookie,轻量 | 低 | 界面简洁,资源占用小 |
| Matomo | PHP + MySQL | MySQL/MariaDB | 可配置 Cookie | 中高 | 功能最全,类 GA |
| Hindsight | Go + React + ClickHouse | ClickHouse | 无 Cookie,事件模型 | 中高 | 查询快,自定义事件强 |
如果你只需要“今天多少人、哪个页面火”,Umami 上手最快;如果你需要一整套完整报表并愿意维护 PHP,Matomo 更合适;Hindsight 则更像“事件分析平台”而不是“网站计数器”,适合愿意稍微多花点部署成本、但换来灵活查询的人。我当时选它,核心原因是看中了事件模型和 ClickHouse 的查询能力,因为后面我想给自己的工具站做几个自定义转化漏斗。
2. 部署前要准备好的几件事
2.1 服务器、域名和基础环境
先列一下我这次部署的底线配置:
- 系统:Ubuntu 22.04 LTS
- 配置:2 核 CPU,4G 内存(2G 也能跑,但 ClickHouse 偶尔会吃紧)
- 磁盘:20G SSD,日志增长不快,但建议预留一半余量
- 域名:单独用
analytics.example.com作为统计入口,别和主站混在一起
我推荐把统计服务放到一个独立子域名,原因有两个:第一,主站服务器和统计服务分开后,将来换主机不用牵连业务;第二,广告拦截器通常只屏蔽常见统计域名,自建域名很少会被误杀,但也别把统计脚本和业务脚本混到一个域名下,否则以后想拆就难了。
基础环境只需要 Docker 和 Compose 插件:
sudo apt update sudo apt install -y docker.io docker-compose-v2 sudo systemctl enable --now docker装完以后确认一下版本,Compose 插件和旧版docker-compose的命令格式略有差别,后面统一用docker compose。
然后去 DNS 控制台加一条 A 记录,把analytics.example.com指向服务器 IP。这一步可以提前做,因为 DNS 生效需要时间,而 Docker 启动很快。
2.2 用 Docker Compose 一次拉起全家桶
Hindsight 官方文档一直推荐用 Docker 部署,我也建议直接用 Compose。我的docker-compose.yml大概是下面这个样子,基于我当时部署的版本整理出来的,镜像名称和 tag 会随版本变化,以官方 README 为准:
services: clickhouse: image: clickhouse/clickhouse-server:23.8 container_name: hs-clickhouse restart: unless-stopped environment: CLICKHOUSE_DB: hindsight CLICKHOUSE_USER: hindsight CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} ulimits: nofile: soft: 262144 hard: 262144 volumes: - ./ch_data:/var/lib/clickhouse hindsight: image: ghcr.io/hindsight/hindsight:latest container_name: hs-web restart: unless-stopped environment: CLICKHOUSE_DSN: clickhouse://hindsight:${CLICKHOUSE_PASSWORD}@clickhouse:9000/hindsight APP_BASE_URL: https://analytics.example.com DISABLE_REGISTRATION: "true" ports: - "127.0.0.1:8080:8080" depends_on: - clickhouse这里有几个细节要解释。
第一,ClickHouse 的ulimits是必填项,默认文件句柄限制不够,启动时容易报 “Too many open files”。如果不加,大概率会在日志里看到一个和nofile相关的错误。
第二,我把 Hindsight 的 Web 端口绑定在了127.0.0.1:8080,而不是直接暴露到公网。因为 HTTPS 和外部访问交给反向代理,这样做可以减少一层攻击面。如果你不会配置反向代理,也可以直接映射8080:8080,但强烈不建议裸奔。
第三,密码不要写在 compose 文件里,用.env文件存放:
CLICKHOUSE_PASSWORD=$(openssl rand -hex 24)然后把生成的随机字符串写进.env,启动时 Compose 会自动读取。之后依次执行:
docker compose up -d docker compose ps docker compose logs -f hindsight看到 Web 服务日志正常输出监听端口后,用curl http://127.0.0.1:8080测试一下。这一步能通,说明应用本身没问题,剩下的就是把它暴露出去。
2.3 上 HTTPS:Caddy 与 Nginx 两种方式
我第一个用的是 Caddy,因为它的 HTTPS 配置真的省心。在服务器上安装 Caddy 后,Caddyfile 里写三行:
analytics.example.com { reverse_proxy 127.0.0.1:8080 }Caddy 会自动申请和续期证书,第一次访问就能带上小绿锁。如果你已经有一套 Nginx,也可以把下面这段塞进对应的 server 块里:
server { server_name analytics.example.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }用 Nginx 的话记得配合 certbot 发证书,不然浏览器会拦得不舒服。无论哪种方式,配置完打开https://analytics.example.com,能看到登录页就算成功了。
2.4 前端接入和分析脚本
接下来是让网站把数据送进来。Hindsight 的接入方式通常是往页面里插入一段脚本,类似这样:
<script async src="https://analytics.example.com/hs.js">script-src 'self' https://analytics.example.com; connect-src 'self' https://analytics.example.com;这个坑我踩过一次:脚本加载了,但事件请求被 CSP 拦下,看板里整整三天是零数据。所以接入后第一件事不是看面板,而是看浏览器控制台有没有报 CSP 错误。
3. 上线后,核心指标与事件怎么用
3.1 熟悉默认看板:别被虚荣指标绑架
打开 Hindsight 看板,默认能看到 PV、独立访客、会话数、跳出率、访问时长、热门页面、来源渠道、设备分布这些常见指标。我的建议是,看数据前先问自己一个问题:我今天要看的是“动作”,还是“结果”。
以博客为例,PV 涨了 50% 听起来开心,但如果来源全是某个社交平台的批量点击,停留时间极短,那这波流量对“读者真正读完文章”这个目标毫无帮助。我更愿意关注“热门页面”和“来源渠道”这两个模块:前者告诉我选题方向,后者告诉我该去哪宣传。跳出率则用来发现页面体验问题——如果一个落地页跳出率长期超过 85%,大概率是首屏没说明白或者加载太慢。
有基础之后,可以进一步用 Hindsight 的自定义筛选功能,把“来自搜索引擎且访问时长超过 60 秒”的会话单独拉出来看。这一步就能把泛流量和“有效流量”区分开。
3.2 埋点一个自定义事件
Hindsight 的事件模型让我最舒服的是埋点不用改后端。以“下载按钮”为例,在前端代码里加一个监听:
document.getElementById('btn-download').addEventListener('click', function () { window.hs && window.hs.track('download_click', { file: 'setup.zip', from: 'landing' }); });用户在页面上的每次点击都会带着事件名和属性发到收集端,之后在后台里选择download_click事件,就能看到这个按钮的点击次数和转化来源。做落地页 A/B 测试时,这个功能比单纯看 PV 有用得多:你可以把方案 A 的按钮埋成cta_a_click,方案 B 埋成cta_b_click,跑一周看数据说话。
再进阶一点,把多个事件串成漏斗。流程是“访问落地页 -> 点击下载按钮 -> 打开注册页 -> 完成注册”,每一步命名成独立事件,Hindsight 的漏斗分析会告诉你每一步流失了多少人。这一步往往能发现真正的产品问题,比如点击很多但注册很少,那问题大概率出在注册表单而不是流量入口。
3.3 会话和留存怎么看
会话是理解用户行为的关键单位。Hindsight 通过会话 ID 把一组相关事件聚合在一起,这样你能看到的不只是“有多少次页面浏览”,而是“访客一次进来做了哪些事”。我一般会关注两类会话:
一类是“单页会话”,进来只看了一个页面就走,这类会话占比高说明内容相关性或站内导航有问题;另一类是“多页会话”,这类用户才是真正在消费内容的人。把这两个比例拉出来对比,比单独看跳出率更明确。
留存分析则更适合产品型站点。比如注册后第 1 天、第 7 天、第 30 天还有多少用户回来,形成队列对比。内容型网站不用太执着于这个指标,因为很多人是搜到某篇文章才来,不订阅不注册也正常。但你如果做的是工具站,留存数据会直接告诉你产品是“一次性工具”还是“日常工具”。
如果你想更暴力一点,可以直接连 ClickHouse 写查询。下面这个是一个示意 SQL,具体表名以你部署的版本为准:
SELECT toDate(timestamp) AS day, uniqExact(session_id) AS sessions FROM events WHERE site_id = 'blog' AND event = 'pageview' GROUP BY day ORDER BY day DESC LIMIT 30;ClickHouse 对这种按天聚合的查询非常擅长,30 天的数据量对个人站点来说基本是毫秒级返回。
3.4 隐私、留存与数据导出
Hindsight 默认不种 Cookie、不做浏览器指纹,这对访客来说确实是更友好的方案。但“没有 Cookie”不代表你可以什么都不写,我的习惯是在网站隐私政策里加一句“本站使用自托管分析服务,不设置跨站 Cookie,仅记录必要的访问事件”。别小看这句话,真遇到较真的用户,它能帮你省很多解释成本。
数据留存方面,个人站点默认配置已经够用,但如果你在意磁盘占用,建议定期清理三个月前的原始事件。ClickHouse 支持按时间删除分区数据,也可以用一条简单的 DELETE 按时间范围清理。自托管的好处就是这种事没人拦你,想留多久留多久,想删立刻删。
导出数据也很重要。Hindsight 后台一般提供事件导出或查询接口,我每月会把原始事件导出一次存到本地备份,因为再可信的数据库也有误删的可能,自己的备份才是最后的保险。
4. 常见问题与排查技巧实录
4.1 接入后数据为零
这是最让人焦虑的问题,但九成都是配置问题。我的排查顺序固定三步:
第一步,打开浏览器控制台,看 Network 里面有没有指向analytics.example.com的请求。如果没有,说明脚本根本没加载,检查一下模板路径和 CSP。第二步,看请求状态码,如果是 404,说明hs.js路径不对或者反向代理没生效;如果是 204/200,说明请求已经进入服务端。第三步,去服务器上看容器日志:
docker compose logs collector找不到 collector 容器的话,就统一看 hindsight 主服务的日志。大多数情况下,日志里会直接写出site_id 不存在或referrer 不允许这类明确错误,照着修就行。
另外,广告拦截插件也可能把自建统计请求拦截掉,尤其是插件规则里包含了较新的分析域名。不过自建域名的命中率远低于 GA,我自己测试下来影响很小,真遇到了临时关掉插件排除即可。
4.2 ClickHouse 起不来或总被 OOM
如果你看到 ClickHouse 容器反复重启,先看日志是不是 “Memory limit exceeded”。2G 内存的机器确实容易触发这个问题,因为 ClickHouse 启动默认会预留较大内存作为查询缓存。应对办法是显式限制内存使用,在配置目录里加一个内存上限文件:
<yandex> <max_server_memory_usage>2147483648</max_server_memory_usage> </yandex>注意不同版本 ClickHouse 的根标签可能叫yandex或clickhouse,以你镜像版本为准。配置完重启容器后,内存占用会稳定在 2G 左右,4G 内存的机器跑起来就非常宽裕了。
如果nofile报错,回到 compose 里的ulimits,确认数值不小于 262144。这个限制不是可选项,而是 ClickHouse 官方文档明确要求的。
4.3 时区不对导致日报数据漂移
现象是看板里的“今日”数据在早晨八点前显示为零,或者日期分组“少一天”。原因很简单:容器默认用 UTC,而你在东八区。
解决办法是在 compose 环境变量里统一加:
environment: TZ: Asia/Shanghai前端看板时区也要同步设置。这事看起来小,但如果不处理,所有日报、周报的日期口径都会错位,后面复盘数据时非常麻烦。我在第一次部署时忽略了这一步,导致第一周的“周一峰值”其实混进了周日的半夜流量,数据比例完全失真。
4.4 数据备份、升级与回滚
自托管就要承担运维责任,备份不能不做。最简单的思路是直接对 ClickHouse 数据目录做快照,容器停止状态下打包:
docker compose stop tar czf hs-backup-$(date +%F).tar.gz ./ch_data docker compose up -d数据量小的话,这个方案完全够用。升级前重复一遍,把备份文件下载到本地,然后再拉新镜像。如果新版本出现兼容性问题,直接把 compose 里的镜像 tag 改回旧版本,从备份恢复数据目录后重新启动,基本就能回到升级前状态。
我现在的习惯是每次升级前都看一眼官方 changelog,确认没有破坏性的 SQL 表结构变更。数据这种东西,平时备份了用不上,等真出事就知道救命。
4.5 referrer 刷量与内网流量干扰
网站上线一段时间后,看板里可能出现一堆“来源是奇怪域名”的流量,这些大概率是来探测或刷量的。别指望它彻底消失,也别被它影响判断。处理方法是先把这类 referrer 加到过滤规则里,再重新看数据。真实用户流量通常集中在搜索引擎、直接访问和少数几个社交平台,突然暴涨的“未知来源”优先怀疑刷量。
内网 IP 的干扰也值得一提。如果你在开发环境里也嵌入了统计脚本,测试人员的访问会污染数据。解决办法是给统计服务配置 IP 过滤,或者建立一个单独的测试站点 ID,把开发测试流量和线上流量彻底分家。
5. 这套方案的影响范围和适用边界
5.1 哪些场景收益最大
跑了几个月之后,我越来越清楚 Hindsight 适合谁。
个人博客是收益最明显的场景:一天几千 PV,数据量不大,ClickHouse 的查询能力完全溢出,换来的是绝对的数据主权和几乎没有的隐私负担。独立开发者的产品站也合适,因为自定义事件让你能低成本追踪注册、激活、付费这些关键动作,不需要给第三方 SaaS 按月交钱。
内部工具和私有系统更推荐。公司内部系统不适合把数据发到外部第三方,自托管分析服务能把所有访问事件留在内网,既满足审计需求,又不用引入外部 Cookie。甚至可以说,只要是你自己能控制服务器的场景,Hindsight 都值得试一次。
5.2 哪些情况不建议用
反过来也说说边界。如果你需要的是会话录屏、热力图、漏斗之外的自动化营销,Hindsight 这类事件分析工具不是正确答案,Matomo 或者商业分析平台会更合适。如果你的团队没人愿意维护 ClickHouse,或者网站访问量已经大到需要集群,那就别为了“自托管”而自托管,分摊运维成本也是成本。
技术上,Hindsight 的部署确实比 Umami 多了一个 ClickHouse,门槛高了一截。这也意味着你选择它的前提,是愿意承担一点点运维成本,来换取查询能力和事件模型上的灵活性。
我自己跑下来的体会是:切换后第一个感觉是首页加载分数上去了,第三个脚本消失了,Cookie 弹窗也没了;第二个感觉是查数据终于不用等转圈。当然,代价是每个月要花点时间看容器状态、做一次备份。对我来说,这笔账非常划算。如果你正好也在纠结统计方案,不妨挑个周末部署一套,让数据告诉你答案。