博客CDN缓存自动化:从发现到修复
博客 CDN 缓存自动化:从发现到修复
起因:Chat 页面缓存问题
事情是这样的,我给博客加了个 AI 聊天功能,页面放在 /chat 路径下。按理说这是个 "use client" 的客户端渲染页面,CDN 不应该缓存才对。
结果发现——CDN 不仅缓存了,还缓存了一年。
排查过程
先看响应头:
x-nextjs-cache: HIT
x-nextjs-prerender: 1
Cache-Control: s-maxage=31536000
31536000 秒,差不多就是一年。关键是这个 header 是 Next.js 自己返回的,不是 CDN 加的。
原因很简单:虽然 /chat 是 "use client" 组件,但 Next.js 仍然对它做了预渲染(生成初始 HTML shell)。页面没有声明 export const dynamic = 'force-dynamic',所以被当成静态页处理了。prerender-manifest.json 里白纸黑字写着:
json
"/chat": {
"initialRevalidateSeconds": false,
"srcRoute": "/chat"
}
false 意味着永不 revalidate,Next.js 就给了个一年的 s-maxage。
修复:revalidate + CDN 自动刷新
最简单的修复是加一行:
ts
export const revalidate = 3600; // 1小时
这样 Next.js 还是预渲染(保留 SEO 优势),但 s-maxage 从一年变成了 3600 秒。
但这只是治标。部署新版本后,CDN 还是会继续缓存旧内容直到 TTL 过期。我需要的是:部署完成后自动刷新 CDN 缓存。
搭建 CDN 自动刷新
方案选型
一开始想在 GitHub Actions 里做,但发现服务器上的腾讯云 SecretId/SecretKey 只在 Docker 容器的环境变量里,GitHub Actions 拿不到。也不想为了这事单独在 GitHub Secrets 里再加一组凭据。
最终方案:服务器上执行。部署脚本里直接调腾讯云 CDN Purge API,凭据从 .env 文件读取。
一开始用 Python 手写了 TC3-HMAC-SHA256 签名,结果踩了一堆坑——服务器 Python 3.6.8 不支持新语法、shell 转义把签名搞坏、PurgeUrlsCache 的 FlushType 参数名和文档不一致。后来换成腾讯云官方 Node.js SDK(tencentcloud-sdk-nodejs),几行代码搞定,本地测试一次通过。
变更感知:不是一把梭
最粗暴的做法是每次部署都全站刷新 CDN。但这样太浪费了——改了个页面组件就得刷全站,没必要。
所以我做了一个变更范围检测:
| 变更路径 | CDN 刷新策略 |
|---|---|
src/components/** |
全站 / |
src/app/layout.tsx / globals.css |
全站 / |
src/app/chat/page.tsx |
只刷 /chat/ |
public/** |
精确 URL |
docs/** / *.md |
跳过 |
GitHub Actions 在 skip-check 阶段已经拿到了 CHANGED_FILES(git diff --name-only HEAD~1 HEAD),把这个列表通过 SSH 环境变量传到服务器,写入临时文件,再交给 purge-cdn.mjs 分析。
实现细节
整个流程是:
push → skip-check (获取 CHANGED_FILES)
→ build-and-deploy
→ SSH 部署 (git pull → 写入变更文件 → deploy.sh)
→ purge-cdn.mjs --changed-file /tmp/.deploy_changed_files
→ analyzeChanges() 按规则匹配
→ 精准刷新对应路由
→ rm /tmp/.deploy_changed_files
purge-cdn.mjs 用腾讯云官方 Node.js SDK 调 CDN Purge API,不用手写签名,代码干净可靠。项目本身是 Next.js,服务器有 Node.js,不需要额外装运行时。
一个意外发现
测试的时候发现服务器上居然没有 .env 文件?凭据全在 Docker 容器的环境变量里。最后发现 .env 在 /www/wwwroot/react.nnnnzs.cn/.env,是 docker-compose.prod.yml 里 env_file 引用的那个路径。之前 find 命令没找到是因为搜索深度不够。
总结
改动涉及 5 个文件:
src/app/chat/page.tsx—export const revalidate = 3600scripts/purge-cdn.mjs— 新增,腾讯云 CDN 缓存刷新 + 变更检测(Node.js SDK 版)scripts/deploy.sh— 新增purge_cdn函数.github/workflows/docker-release.yml—CHANGED_FILES传递链路.gitignore— 排除部署临时文件
服务器上跑了一轮测试,API 调用成功,变更检测逻辑正常。下次部署就自动生效了。
这种「部署后自动清理 CDN」的模式,感觉每个用 CDN 的项目都应该有一套。毕竟用户不应该为你的部署流程买单——你更新了,他就应该看到最新的。