Appearance
Umami —— 自托管 Web 流量分析(JavaScript 埋点统计)
最后更新:2026-08-21
这个工具是做什么的
GoAccess 靠读 nginx access.log 回答"有多少访客、看了哪些页面、来自哪里",而 Umami 走的是完全相反的一条路:在网页里嵌入一段 JavaScript(script.js),由浏览器在每次页面访问时主动把事件上报到 Umami 后端。它不碰服务器日志,属于**客户端埋点(Client-Side Tracking)**流派,是自托管、隐私友好、免 Cookie 的轻量版 Google Analytics。
本文重点是原理:这套系统里有谁(实体)、数据怎么流动(数据流)、浏览器到后端怎么通信(通信过程),并在最后与 GoAccess 做横向对比。
一、实体模型:这套系统里有谁
1.1 实体清单
| 实体 | 角色 | 关键属性 / 职责 |
|---|---|---|
| 浏览器 | 事件生产者 | 加载页面 → 注入脚本执行 → 主动上报 pageview / 事件 |
| script.js | 采集脚本 | 用 data-website-id 关联站点;按需 POST 到 /api/send |
| 站点 HTML | 埋点载体 | <script src="https://umami.geek-doc.cn/script.js" data-website-id="xxx"></script> |
| Umami 前端 | Next.js 应用 | 提供管理后台(Dashboard)与采集接口(/api/send) |
| Umami API | 后端服务 | 鉴权、校验 payload、写入数据库、供前端查询统计 |
| PostgreSQL | 持久化存储 | 存事件、会话、访客、站点配置;与 GoAccess 的无数据库形成对比 |
| nginx | 反向代理 | 把 umami.geek-doc.cn 请求反代到 127.0.0.1:3000,提供 HTTPS |
1.2 实体关系

核心结论:Umami 是有数据库的——浏览器 → script.js → /api/send → nginx → Umami → PostgreSQL,事件逐条落库,这是它与 GoAccess(无数据库、内存哈希聚合)最本质的区别。
二、数据流:一次页面访问怎么变成一条记录
2.1 埋点脚本的加载
浏览器打开站点页面时,HTML 里有这样一段:
html
<script defer src="https://umami.geek-doc.cn/script.js" data-website-id="595b3967-..."></script>src指向 Umami 的采集脚本(由 nginx 反代提供)data-website-id是这个站点在 Umami 里的唯一 ID,用于把事件归到正确站点defer让脚本在文档解析后、DOMContentLoaded前执行,不阻塞渲染
2.2 pageview 事件上报(核心链路)

上报的 payload 结构(v3 版):
json
{
"type": "event",
"payload": {
"website": "595b3967-6102-427a-816b-c751d7c02f39",
"event_type": "pageview",
"url": "/tools/cpu/top",
"referrer": "https://www.google.com/",
"hostname": "geek-doc.cn",
"language": "zh-CN",
"screen": "1920x1080",
"title": "性能分析工具速查"
}
}2.3 会话(Session)是怎么组织的
- 每个访客首次访问会新建一个 session,并用一个标识(session/visit token)记住
- 之后该访客的后续事件都挂到这个 session 下,直到会话超时(默认 30 分钟无活动)才开新会话
session表记录浏览器、OS、国家、设备等维度信息;event表记录每次 pageview / 自定义事件
| 表 | 存什么 | 说明 |
|---|---|---|
website | 站点配置 | id(对应 data-website-id)、域名、时区 |
session | 会话维度 | 访客的一次连续访问:浏览器/OS/国家/设备/时长 |
event | 事件明细 | 每次 pageview、事件类型、URL、时间戳 |
对比 GoAccess:GoAccess 在内存哈希表里"边读边累加",落盘只有最终 HTML;Umami 把每条事件落进 PostgreSQL,所以能支持任意时间段的自定义查询、留存分析、实时曲线——代价是需要一个数据库来承载。
三、通信过程:管理与采集两条通道
3.1 采集通道:浏览器 → nginx → Umami
script.js 从浏览器向 umami.geek-doc.cn 发请求,经 nginx 反代进入容器:
| 点 | 说明 |
|---|---|
| 走 HTTPS | 域名配了 Let's Encrypt 证书,浏览器到 nginx 全程加密,无 Cookie、无明文指纹 |
| 反代路径 | nginx 把 /api/send 等请求反代到 127.0.0.1:3000(容器内 Umami) |
| CORS | script.js 是跨域加载的,Umami API 需返回允许的 Access-Control-Allow-Origin 让浏览器能读响应 |
| 无状态 | 采集接口不依赖登录态,任何人加载页面都会上报,天然适合匿名统计 |
3.2 管理通道:浏览器 → nginx → Umami 后台
管理后台是另一个入口 https://umami.geek-doc.cn/(同一个域名、同一套反代),登录后能看到 Dashboard:

注意:管理后台页面不含
script.js埋点,所以"登录后台"这个动作本身不会被统计——这是设计使然(避免把自己灌进数据里),也是很多人后台看 Overview 为 0 的原因之一(另一个是还没用浏览器访问过主站页面)。
四、GoAccess vs Umami:两种统计流派
两个工具都回答"站点访问情况",但数据源、架构、适用场景完全不同:
| 维度 | GoAccess | Umami |
|---|---|---|
| 数据源 | 服务器日志(nginx access.log) | 客户端 JS 埋点(浏览器上报) |
| 存储 | 无数据库,内存哈希聚合,落盘 HTML | PostgreSQL 落库 |
| 部署 | 单进程读日志 | 前端 + 后端 + 数据库(docker compose) |
| 精确度 | 能统计所有请求(含爬虫、curl、API) | 只统计执行了 script.js 的浏览器 |
| 爬虫 | 能看到(需 --ignore-crawlers 过滤) | 默认看不到(爬虫不执行 JS) |
| 资源开销 | 极低(读文本) | 需维护一个数据库 + 应用容器 |
| 实时性 | WebSocket 秒级 | 秒级(落库后查询) |
| 隐私合规 | access.log 含 IP/UA 原文 | 无 Cookie、可匿名化 |
| 维度 | 依赖日志字段(IP/UA/URL/referer/status) | 前端可采集(UA/screen/language/自定义事件) |
| 典型用途 | 流量大盘、防爬监控、带宽统计 | 真实用户行为、转化、留存、渠道分析 |
互补关系
- GoAccess 管"机器视角":所有打进服务器的请求都算,适合看流量规模、被爬虫扫、带宽成本——"量"。
- Umami 管"人视角":只算真正执行了 JS 的真实浏览器,适合看用户行为、来源渠道、页面停留——"质"。
- error.log 管"病":服务端 5xx、限流、超时,两者都看不到,需独立看日志。
实践建议:全站监控用 GoAccess 做流量仪表盘 + 防爬;用 Umami 做真实用户的行为分析。两者数据可对照——若 GoAccess 显示访问暴涨而 Umami 平稳,多半是爬虫/采集脚本在刷。
五、部署形态(本站实践)
本站 Umami 部署在容器里,由 nginx 反代对外,链路如下:
text
浏览器 → https://umami.geek-doc.cn → nginx(443, Let's Encrypt) → 127.0.0.1:3000 → Umami 容器
└→ PostgreSQL 容器| 层 | 配置要点 |
|---|---|
| nginx 反代 | proxy_pass http://127.0.0.1:3000; + X-Forwarded-* 头,转发 Host/Proto |
| TLS | 独立 Let's Encrypt 证书,每日 cron 自动续期 + reload |
| 端口暴露 | 容器只监听 127.0.0.1:3000,公网不可直连,必须经反代 |
| 埋点 | 主站 config.ts head 注入 <script src=".../script.js" data-website-id="..."> |
| 凭据 | 管理后台账号存于服务器 root 只读文件,不写入文档 |
六、常见坑
| 坑 | 说明 |
|---|---|
| 后台 Overview 为 0 | ① 还没用浏览器访问过主站页面(不是后台);② 后台页面本身不含埋点;③ 时间区间/时区没选对(容器默认 UTC) |
| 时区不一致 | Umami 容器时区默认 UTC,与宿主 CST(+8) 不同,看统计时注意按正确时区切换 |
| 爬虫不计入 | Umami 只统计执行了 JS 的浏览器,curl/脚本/搜索引擎爬虫默认不进数据——这不是 bug |
| 跨域采集 | script.js 从主站跨域加载到 umami.geek-doc.cn,需保证 API 响应正确的 CORS 头 |
| 网站 ID 不匹配 | 埋点里的 data-website-id 必须与后台网站的 id 一致,否则事件被丢弃 |
| 广告拦截器 | 部分拦截插件会屏蔽第三方统计脚本,导致该用户不计数(GoAccess 不受影响) |
七、交叉引用
- 同类工具对比:GoAccess —— 服务器日志分析的流量仪表盘,与 Umami 是互补的两条统计路线
- 反代与证书:Umami 由 nginx 反向代理 + Let's Encrypt 证书提供 HTTPS,与主站共用一套反代架构
- 防爬监控:Umami 的"人视角"需配合 GoAccess 的"机器视角"来区分真实用户与爬虫
- 事后复盘:sar 看系统资源历史,GoAccess/Umami 看访问流量历史
一句话总结:Umami 是一条"浏览器埋点 → 事件上报 → 落库 → 查询渲染"的 Web 流量分析链路——靠网页里一段
script.js采集真实用户行为并写入 PostgreSQL,由 nginx 反代 + HTTPS 提供接入,与靠读服务器日志的 GoAccess 形成"人视角 vs 机器视角"的互补,是自托管、隐私友好、免 Cookie 的轻量版 Google Analytics。