mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1773 字
5 分钟
用 Cloudflare Worker 给 Astro 博客接入 Umami 访客统计
2026-05-12

用 Cloudflare Worker 给 Astro 博客接入 Umami 访客统计#

今天给博客补上了 Umami 统计:页面访问由 Umami 脚本记录,统计读取则通过 Cloudflare Worker 做一层安全代理。这样前端既能显示首页历史访客数和文章页浏览量,又不会把 Umami API Key 暴露到浏览器里。

这篇文章记录完整接入过程。文中的域名、网站 ID 和密钥都使用占位符,照着做时请替换成自己的配置。

先在 Umami Cloud 里准备网站和 API Key#

开始写代码之前,先把 Umami Cloud 后台里的几件事做完。这里不需要把任何密钥写进博客仓库,只要拿到后面配置 Worker 会用到的三个值:

UMAMI_API_URL=https://api.umami.is
UMAMI_WEBSITE_ID=<YOUR_WEBSITE_ID>
UMAMI_API_KEY=<YOUR_UMAMI_API_KEY>

1. 添加网站#

登录 Umami Cloud 后,进入 Websites 页面,点击添加网站。通常需要填写:

  • 网站名称:方便自己识别即可,比如 My Blog
  • 网站域名:填写正式访问域名,比如 example.com
  • 时区:按自己主要统计口径选择

保存后,Umami 会创建一个网站条目。这个条目对应后面统计脚本里的 data-website-id

2. 获取 Website ID#

进入刚刚创建的网站详情页,找到网站设置或脚本安装区域。Umami 会给出类似这样的安装脚本:

<script
defer
src="https://cloud.umami.is/script.js"
data-website-id="<YOUR_WEBSITE_ID>"
></script>

其中 <YOUR_WEBSITE_ID> 就是网站 ID。它不是 API Key,但教程和截图里仍建议打码,避免把自己的站点配置原样暴露出来。

3. 生成 API Key#

在 Umami Cloud 的账户设置里找到 API Keys,创建一个新的 API Key。建议命名成容易识别的用途,例如:

blog-stats-worker

生成后只复制一次,立刻保存到 Cloudflare Worker Secret:

UMAMI_API_KEY=<YOUR_UMAMI_API_KEY>

不要把这个值写进 Astro 代码、Markdown 文章、GitHub 仓库或浏览器端脚本。后续所有读取统计的请求都应由 Worker 代发。

4. 在 Worker 里配置变量#

Cloudflare Worker 里分别配置:

Variables:
UMAMI_API_URL=https://api.umami.is
UMAMI_WEBSITE_ID=<YOUR_WEBSITE_ID>
Secrets:
UMAMI_API_KEY=<YOUR_UMAMI_API_KEY>

UMAMI_API_URL 用 API 域名,UMAMI_WEBSITE_ID 用网站 ID,UMAMI_API_KEY 放 Secret。这样前端请求 Worker 时,只能拿到裁剪后的统计结果,拿不到真正的 API Key。

为什么要加 Worker 代理#

Umami 的统计脚本本身可以直接放在网页里:

<script
defer
src="https://cloud.umami.is/script.js"
data-website-id="<YOUR_WEBSITE_ID>"
></script>

这段脚本只负责“记录访问”。如果想在自己的页面上显示统计数据,比如“历史访客数”或“当前文章浏览量”,就需要调用 Umami API。

问题在于:Umami Cloud API 需要 API Key。这个 Key 不能写进前端,否则任何人打开浏览器开发者工具都能看到。

所以结构变成这样:

浏览器
-> Cloudflare Worker
-> Umami Cloud API
-> Worker 返回安全裁剪后的统计结果
-> 页面显示 views / visitors

前端只知道 Worker 地址,不知道 API Key。

第一步:在全站 Layout 加载 Umami 脚本#

Astro 中可以用 import.meta.env.PROD 判断是否是生产环境。这样本地开发时不会加载统计脚本,也不会污染访问数据。

示例:

---
const isProduction = import.meta.env.PROD;
---
{
isProduction && (
<script
is:inline
defer
src="https://cloud.umami.is/script.js"
data-website-id="<YOUR_WEBSITE_ID>"
></script>
)
}

这里的 data-website-id 不是 API Key,但仍建议不要在文章里写自己的真实值。公开代码仓库中它通常不可避免会出现,写教程时用占位符即可。

第二步:准备 Cloudflare Worker 环境变量#

在 Cloudflare Worker 里配置:

Secrets:
UMAMI_API_KEY=<YOUR_UMAMI_API_KEY>
Variables:
UMAMI_API_URL=https://api.umami.is
UMAMI_WEBSITE_ID=<YOUR_WEBSITE_ID>

注意:UMAMI_API_URLhttps://api.umami.is,不要填 https://cloud.umami.is。前者是 API 域名,后者是统计脚本和管理页面相关域名。

第三步:实现 Worker 统计代理#

最终可用的 Umami Cloud stats endpoint 是:

https://api.umami.is/v1/websites/<YOUR_WEBSITE_ID>/stats

Worker 里拼接时,可以兼容用户把 UMAMI_API_URL 写成 https://api.umami.is/v1 的情况,所以先去掉结尾的 /v1

const apiBase = env.UMAMI_API_URL.replace(/\/$/, "").replace(/\/v1$/, "");
const url = new URL(`${apiBase}/v1/websites/${env.UMAMI_WEBSITE_ID}/stats`);

认证头使用:

{
"x-umami-api-key": env.UMAMI_API_KEY
}

Worker 最后只返回前端需要的字段:

{
"ok": true,
"views": 123,
"visitors": 45
}

不要把 rawStats、请求头、API Key 或账户信息返回给前端。调试阶段可以临时返回上游状态码,但上线后要删掉。

第四步:读取 pageviews 和 visitors#

Umami 的返回结构可能因版本不同略有差异。有的字段直接是数字,有的可能是对象:

function num(value) {
return Number(value?.value ?? value);
}
const views = num(stats.pageviews ?? stats.views);
const visitors = num(stats.visitors);

这样无论返回:

{ "pageviews": 3, "visitors": 2 }

还是:

{ "pageviews": { "value": 3 }, "visitors": { "value": 2 } }

都能正常解析。

第五步:给前端做一个通用 Stats 组件#

前端组件只做三件事:

  1. 读取当前 window.location.pathname
  2. 请求 Worker:<WORKER_URL>/?path=<CURRENT_PATH>
  3. 根据需要显示 viewsvisitors

示例:

const url = new URL("<YOUR_WORKER_URL>");
url.searchParams.set("path", window.location.pathname || "/");
const response = await fetch(url, {
headers: { Accept: "application/json" },
});
const data = await response.json();

首页使用 visitors,文章页使用 views

第六步:首页显示历史访客数#

站点统计卡片里新增一行:

历史访客数 <visitors>

样式直接沿用原来的站点统计 widget:图标、字号、颜色和间距都保持一致,这样不会让统计功能像“硬塞进去”的外部模块。

移动端如果使用 drawer 或侧边栏折叠面板,需要确认站点统计组件也被加入移动端组件列表。我的做法是把站点统计放在移动端抽屉里的倒数第二个,刚好显示在友情链接之前:

drawer: [
"profile",
"announcement",
"music-sidebar",
"categories",
"tags",
"site-stats",
"friend-links",
]

第七步:文章页显示当前文章浏览量#

文章页可以在发布日期、分类、标签这些 meta 信息旁边追加浏览量:

浏览量 <views>

关键点是查询路径要等于当前文章的真实 URL path,例如:

/posts/example-post/

如果文章 permalink 有自定义规则,尽量用 window.location.pathname,不要手写 slug 拼接。这样以后改链接结构时,统计查询也不会跟着坏掉。

第八步:几个踩坑点#

1. cloud.umami.isapi.umami.is 不是一回事#

统计脚本来自:

https://cloud.umami.is/script.js

API 请求使用:

https://api.umami.is

把 API URL 写成 cloud.umami.is 会遇到 404 或 HTML 错误页。

2. Cloud API 的可用 stats 路径#

这次最终确认可用的是:

/v1/websites/<YOUR_WEBSITE_ID>/stats

不要拼成 self-hosted 风格的:

/api/websites/<YOUR_WEBSITE_ID>/stats

也不要在 https://api.umami.is 后漏掉 /v1

3. startAt=0 不一定是好主意#

我最后使用站点创建附近的时间作为默认起点:

const START_AT = Date.parse("2026-05-11T00:00:00.000Z");

实际使用时可以换成你的网站上线日期。这样历史统计范围更明确,也避免请求一个过大的时间跨度。

4. 调试信息要及时删掉#

调试 Worker 时,可以临时返回:

{
"upstreamStatus": 200,
"upstreamUrl": "...",
"rawStats": {}
}

但上线后建议只返回:

{
"ok": true,
"views": 3,
"visitors": 2
}

统计数据本身通常不是敏感信息,但账户结构、上游返回原文和内部 URL 没必要长期暴露。

最终效果#

接入完成后:

  • Umami 脚本只在生产环境记录访问
  • API Key 只存在 Worker Secret 里
  • 首页能显示历史访客数
  • 文章页能显示当前文章浏览量
  • 本地开发不会污染统计
  • 移动端也能看到站点统计面板

这套方案比较适合 Astro + Cloudflare Pages + Umami Cloud 的组合。核心原则很简单:记录访问可以放前端,读取统计要走服务端代理;公开教程和提交记录里,只写结构和占位符,不写自己的密钥与账户信息。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

用 Cloudflare Worker 给 Astro 博客接入 Umami 访客统计
https://yuulog.org/posts/umami-cloud-worker-stats-guide/
作者
星轨手札
发布于
2026-05-12
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录