- blog-admin tools.ts: favicon 响应加 s-maxage/CDN-Cache-Control, 让 CF 边缘可缓存,边缘命中即不进 Worker,KV 读取量大幅下降 - scripts/refresh_cdn.js: 裸域名自动补尾斜杠(多吉云 rtype=path 是目录刷新, https://usj.cc 只刷首页,https://usj.cc/ 才是全站),并打印刷新清单 - 新增文档:评论孤儿修复方案、函数版证书管家方案、诊断报告
14 KiB
四问诊断报告(2026-10-06)
一次性回答四个问题:CDN 刷新、KV 配额、D1 评论对账、certimate 迁移。
一、多吉云刷新 & 已删文章还能访问
1.1 先更正我自己的话
我一开始说「多吉云那条已经是全量(--all)」——这句话是错的,实测证据如下:
cnb-secrets.yml:33 CDN_URL_LIST: "https://usj.cc"
scripts/refresh_cdn.js 只做一件确定性的事:把 CDN_URL_LIST 按逗号/换行拆开,
逐条调 /cdn/refresh/add.json(rtype: "path")。当前配置里这个变量只有首页一个 URL。
结论:多吉云确实不是全量刷新,只刷新了首页 https://usj.cc。
1.2 那为什么删掉的文章还能访问?
不是多吉云的问题。实测这条 URL:
$ curl -sI https://usj.cc/202610021614.html
HTTP/1.1 200 OK
Server: marco/3.2 ← 又拍云特征头
X-Cache-Lookup: Hit From Upstream Cluster ← 命中缓存
Last-Modified: Sun, 04 Oct 2026 14:12:26 GMT ← 旧副本时间
Cache-Control: max-age=691200 ← 8 天 TTL
Age: 131684 ← 已缓存约 1.5 天
判别实验(关键):
| URL | 状态 | 说明 |
|---|---|---|
/202610021614.html(已删除) |
200 | 缓存里还有旧副本 |
/zzz-not-exist-111.html(从未存在) |
302 → /404.html | 源站没这个文件 |
两者行为不同 ⇒ 说明源站确实已经没有这个文件了(本地 content/ 和 public/ 均已确认无此页),
现在返回 200 完全是又拍云上的 8 天旧副本。
1.3 根因:两条 CDN 线路都刷不到「已删除页」
scripts/purge_list.js 的逻辑是:遍历 构建产物 public/,把里面所有 .html 列出来提交刷新。
// 全量时 walkHtml('public') —— 遍历构建产物
// 已删除的页面自然不在里面 → 永远不会被提交刷新
也就是说:刷新清单只会包含「现在存在的页面」。一个页面被删掉后, 它就从清单里消失了,于是 CDN 上的旧副本只能等 TTL 自然过期。
- 又拍云:HTML
max-age=691200= 8 天 - 多吉云:只刷首页,问题更明显
1.4 处置建议
| 方案 | 说明 | 成本 |
|---|---|---|
| A. 什么都不做 | 等 8 天自然过期 | 0 |
| B. 手动刷新一次(推荐) | 把已知的已删 URL 提一次刷新,立刻生效 | 一次性 |
| C. 流水线加「删除清单」 | 构建时对比上一次产物,把消失的 .html 追加进 purge 清单 |
改脚本 |
对已删文章这种低频事件,B 足够。需要的话我可以立刻把已知的那几个已删 URL 提交到又拍云+多吉云。
二、KV 配额告警:需要调整吗?
2.1 告警内容
已使用 Workers KV 免费套餐每日限制的 50%,超限将返回 429;重置时间 2026-10-06 00:00 UTC。 免费额度:读 100,000/天、写 1,000/天、删 1,000/天、list 1,000/天。
2.2 谁在吃配额?(实测 4 类来源)
| 来源 | 代码位置 | 频率 | 影响 |
|---|---|---|---|
| 评论数缓存 | comments.ts:180/206 |
每次评论列表请求,miss 时写(TTL 600s) | 读 + 写 |
| 评论数版本号 | comments.ts:53/61 |
评论增删时写 | 写 |
| friend-link favicon | routes/rss/tools.ts:86 |
每次访问友链/友圈页,每个友链一次 | 读(大户) |
| 人机验证 / 通知节流 | human.ts / admin-notify.ts |
低 | 少量 |
读数最大的就是 favicon 接口。 友链共 54 条(可见 42 条),其中 15 条头像走 /api/favicon:
// circles.html / linkify.js
img.src = RSS_API_BASE + '/api/favicon?url=' + encodeURIComponent(target);
每刷一次友圈/友链页 → 触发 N 次 /api/favicon → 每次至少 1 次 KV get。
favicon 本体有 24h 浏览器缓存,但首访、清缓存、换设备都会重新打。
2.3 建议:暂时不用调整,但建议做一个小优化
- 读 50% 是「接近」不是「超了」,且每天 UTC 0 点清零。按当前站点的访问量,正常不会打穿。
- 即使打穿,后果是
/api/favicon走兜底字母图、评论数退化为实时查库——不是灾难性故障。 - 免费版写限额 1,000/天,比读更容易被评论数缓存写穿。真正的风险点是评论突然暴涨或有人刷评论。
可做的小优化(成本低、收益直接):
- favicon 接口响应已经带了
Cache-Control: max-age=86400,可以加一层 Cloudflare Cache Rule, 让/api/favicon*在边缘缓存 24h,这样 KV 读基本归零。 - 若担心写限额,把
cml:ver:的写入合并/延迟(比如 5 分钟内同页重复 bump 只写一次)。
要不要升级到付费($5/月)? 目前不需要。等到真收到「已超限」而不是「接近」的邮件再说。
三、D1 评论对账:老文章评论为什么没了
3.1 先说结论(这个和你想的不一样)
原库 3979 条评论 / 线上 3423 条。乍看差 556 条,但拆开看,「缺失」的绝大部分根本不是评论:
| 项 | 数量 |
|---|---|
| 线上缺失合计 | 711 条 / 63 页 |
其中 点赞型记录(内容含 [LIKE]) |
494 条(69%) |
| → 真实评论型缺失 | 217 条 |
关键发现:线上 [LIKE] 记录为 0 条。
$ SELECT SUM(CASE WHEN content LIKE '%[LIKE]%' THEN 1 ELSE 0 END) FROM comments;
→ 0
说明当年数据导入时,已经主动过滤掉了所有点赞型记录——这是正确的设计(点赞不该占用评论表)。 所以拿原库和线上直接比条数,本身就会假性多出 494 条。
3.2 缺失的 217 条真实评论,按站点拆
| 站 | 缺失 | 其中点赞 | 真实 |
|---|---|---|---|
| 伍比贰 | 521 | 431 | 90 |
| 优世界 | 101 | 0 | 101 |
| 演示站 | 48 | 41 | 7 |
| 往后 | 41 | 22 | 19 |
注意:「伍比贰」「演示站」「往后」是另外的站点,不属于 usj.cc。 它们的数据本来就不该进本站。
3.3 「优世界」那 101 条对得上现站吗?
逐页核对,结果是 一页都对不上:
| page_key | 条数 | 现站情况 |
|---|---|---|
/yl |
48 | 页面不存在(是 2022 年的旧友链页) |
/20241018-some-useful-win-tools |
12 | 文章还在,但 slug 已改为 20241019,且是草稿(未发布) |
/27、/13、/m/17 等 |
41 | 不存在(旧站的分页/短链) |
/20240921-clock-in |
5 | 文章已删除 |
根因不是「key 对不上」,而是这些文章本身已经不存在了。 评论是挂在「已删除文章」上的,key 再怎么规范化也接不回来。
3.4 站上现存页面 vs 线上 —— 有没有真的错位?
没有。 用现站 284 个 slug 逐一匹配线上缺失的 63 个 key:
能对上现存页面: 1 页 / 4 条 (只有 /about.html)
对不上 : 62 页 / 707 条
唯一一个疑似错位:/about.html(4 条)——但深挖后发现它也不需要修:
id=14229 /about.html [伍比贰] 2026-02-07 👍 已点赞 Cool [LIKE]
id=14268 /about.html [伍比贰] 2026-02-12 👍 已点赞 Interesting [LIKE]
id=14269 /about.html [伍比贰] 2026-02-12 👍 已点赞 很棒的文章! [LIKE]
id=14276 /about.html [伍比贰] 2026-02-13 👍 已点赞 写得很好 [LIKE]
这 4 条全是点赞型记录,而且属于**「伍比贰」站**,本就不该进本站。
→ 结论:线上 D1 没有任何错位,完全不需要修复。
3.5 线上「多出」的 7 页是什么?
| page_key | 条数 |
|---|---|
/20260630162329.html |
25 |
/20260724101237.html |
24 |
/20211001.html |
14 |
/20261005113439.html |
13 |
/20260629223659.html |
10 |
/202610011226.html |
2 |
/20261005213010.html |
2 |
这些是导出 db 之后新增的评论(2026-06 之后),属于正常增长,不用动。
3.6 修复建议
结论:线上 D1 不需要做任何修复。
你的直觉「很多评论没跟页面 key 对应上、老文章没评论了」—— 真实情况是:那些评论对应的文章,本身已经不在了(已删除 / 仍是草稿 / 属于别的站), key 再怎么规范化也接不回来。线上数据是干净的。
不建议做的:
- ❌ 批量把「伍比贰/演示站/往后」的评论导进来 —— 那是别的站的数据(含大量点赞记录)
- ❌ 把
/yl、/27等改成别的 key —— 没有正确的目标,改了反而污染 - ❌ 恢复 494 条点赞记录 —— 线上设计本就不存点赞(
[LIKE]记录当前为 0)
如果你更看重「把老评论展示出来」,唯一的正路是重建这些页面 (把已删文章从 git 历史恢复,或用 slug 重定向)。这个要单独评估,工作量比改 key 大得多。
但请注意:即使恢复了页面,能救回的也只是极小一部分——
「优世界」站真实缺失的 101 条里,48 条属于 /yl(2022 年旧友链页),
41 条属于 /27 /13 /m/17 等旧站分页短链,只有 12 条是正经文章评论。
3.7 那「评论少了」的体感从哪来?
很可能来自这两点(都正常):
- 点赞不再算评论:原库 494 条点赞在导入时被过滤,前端看到的评论数自然变少。
- 老文章页面已删:文章没了,评论区自然也没了。
如果确实想提升「评论数」,更实际的做法是在新文章上做引导,而不是抢救旧数据。
四、certimate 能否迁到 Cloudflare
4.1 你现在的架构
certimate(跑在某台服务器上)
├─ 申请:litessl CA,*.usj.cc + usj.cc,DNS-01,tencentcloud-dns
├─ 部署:dogecloud-cdn(多吉云 CDN)
└─ 部署:1panel website(那台服务器上的站点)
定时:10 12 * * *(每天 UTC 12:10)
失败邮件:177018615@qq.com
当前线上证书实测:
issuer = LiteSSL RSA CA 2025(TrustAsia)
subject = CN=usj.cc
SAN = usj.cc, *.usj.cc
有效期 = 2026-09-08 → 2026-12-07
4.2 certimate 支持 Cloudflare 吗?
支持,而且支持两种角色:
| 角色 | 支持 | 说明 |
|---|---|---|
| DNS provider(申请用) | ✅ | 填 Cloudflare API Token 即可,用于 DNS-01 验证 |
| Deploy provider(部署用) | ✅ | 可将证书部署到 Cloudflare |
(certimate 官方:60+ DNS 托管商、120+ 部署目标,Cloudflare 在列。)
4.3 你的真实目标:「把服务器干掉」
需要先厘清一件事:usj.cc 现在到底谁在提供 HTTPS?
从实测响应头看:
Server: marco/3.2 ← 又拍云
Via: T.206.M, V.403-zj-fud-202, ... ← 又拍云多级节点
对外服务的是又拍云 CDN,不是那台服务器。那台服务器承担的是:
- 跑 certimate(证书申请+分发)
- 1Panel 上的 write-server / editor-api 等自建服务
所以「干掉服务器」要分清两件事:
① 只干掉「证书维护」这件事 → 完全可以,用 Cloudflare 托管 DNS + certimate 的 CF provider。
路径:
- 把
usj.cc的 DNS 托管迁到 Cloudflare(当前在腾讯云 DNSPod) - certimate 里把「DNS 提供商」从
tencentcloud-dns换成cloudflare(填 CF API Token) - 证书申请照旧(litessl 或换 Let's Encrypt),DNS-01 走 CF API
- 部署目标改为「Cloudflare」(如果把站点也迁到 CF)或保留又拍云/多吉云
② 连「站点托管」也迁到 Cloudflare → 可行但有取舍:
| 优点 | 缺点 |
|---|---|
| CF 免费版自带 Universal SSL(免申请免续期) | 国内访问速度不如又拍云/多吉云(CF 免费版节点在境外) |
| 不用再管 90 天续期 | 免费版 Universal SSL 只覆盖 example.com + *.example.com |
| 有 CF Origin CA(15 年有效期)给回源用 | 想用 CF 自签源站证书需另配 |
4.4 ⚠️ 关键提醒:国内访问速度
你的读者主要在国内。又拍云/多吉云是国内 CDN,Cloudflare 免费版是境外节点—— 如果为了省一台服务器而把整个站点切到 CF,国内访问大概率变慢(首包延迟从几十 ms 变成几百 ms)。
所以我建议:
- ✅ 可以做的:把
usj.cc的 DNS 托管迁到 Cloudflare(CF DNS 免费、管理方便、API 完善), certimate 用它做 DNS-01,证书照旧签发后部署到又拍云 + 多吉云。这样证书维护不再依赖服务器上的 1Panel 部署环节。 - ⚠️ 建议保留又拍云/多吉云做 CDN,不要为了「干掉服务器」把站点也搬到 CF。
- ❌ 如果那台服务器还跑着 write-server / editor-api,那就不能关——关之前先确认没有别的服务在跑。
4.5 你现在到底能不能关服务器?
需要先回答这个问题:那台服务器上除了 certimate,还跑着什么?
- 如果只有 certimate → 迁完就能关 ✅
- 如果还有 write-server / editor-api(按记忆应该有)→ 关不掉 ⚠️
建议你在关之前先列一下那台机器上的服务清单。需要的话我可以帮你连上去盘一遍。
附:本次用到的排查命令
# CDN 缓存判别
curl -sI https://usj.cc/202610021614.html | grep -iE "server|age|last-modified|x-cache"
# D1 查询(curl 走 IPv4,Node fetch 会 ENETUNREACH)
curl -s -X POST \
"https://api.cloudflare.com/client/v4/accounts/$ACC/d1/database/$DB/query" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"sql":"SELECT page_key, COUNT(*) FROM comments GROUP BY page_key"}'
# 线上是否有点赞记录
# → SELECT SUM(CASE WHEN content LIKE '%[LIKE]%' THEN 1 ELSE 0 END) FROM comments; → 0