AI 生成的站点接了 CDN 还是慢,或者改了内容用户看到的还是旧版,八成是缓存头没分层。核心结论只有一句:带指纹的静态资源给一年长缓存,HTML 一律 no-cache,两者之间的东西按更新频率单独定。剩下所有配置都是这条规则的展开。
先分三类,再谈配置
配缓存最常见的错误是对全站套一条统一规则。按资源性质分三类,各自策略完全不同:
| 资源类型 | 例子 | 建议响应头 | 理由 |
|---|---|---|---|
| 带指纹的静态资源 | app.4f3a9c.js、hero.deadbeef.png | Cache-Control: public, max-age=31536000, immutable | 内容永不变更,URL 变了就是新资源,可以放心长缓存 |
| HTML 入口 | /、/about/、文章页 | Cache-Control: no-cache(或 max-age=0, must-revalidate) | HTML 是引用清单,它一旧,引用的资源跟着全旧 |
| 无指纹的普通文件 | /uploads/2026/report.pdf、用户上传图片 | public, max-age=3600 起,按更新频率调 | URL 不变但内容会换,只能靠 TTL 兜底 |
这里有个容易忽略的点:不设 Cache-Control 不等于不缓存。缓存允许按启发式规则自行决定缓存时间,结果不可控。只要对这个资源有明确要求,就必须显式写下响应头。
指纹化是长缓存的前提,不是可选项
没有指纹就给一年缓存,等于宣布这个 URL 一年内不能改。所以正确顺序是:先让构建工具给文件名加内容哈希,再配长缓存。
Webpack 、 Vite 这类工具默认就能做。 AI 生成的站点如果不带构建步骤,最省事的替代是给 URL 加查询串版本号,例如 /assets/hero.png?v=deadbeef——效果等价,只是部分 CDN 需要额外配置才会把查询串纳入缓存键。
注意一条铁律:更新时换新 URL,不要覆盖旧文件。覆盖旧文件意味着已经缓存旧版本的 CDN 节点和用户浏览器不会回源,你会收到一批”我这边还是旧的”的反馈,而且无从下手。
HTML 为什么必须 no-cache
HTML 里写着它引用了哪些 CSS 和 JS 。如果用户浏览器缓存了旧 HTML,它就会继续请求旧指纹的资源——那些资源还在 CDN 上,一切正常,用户看到的就是上一个版本。这个故障最难排查,因为从服务端看没有任何异常。
no-cache 的含义是”可以存,但用之前必须回源校验”,不是”不要存”。校验通过时源站返回 304,正文不传输,开销很小。真正”不许存”的是 no-store,用在含个人信息的响应上。
CDN 侧:缓存键、 s-maxage 与陈旧服务
浏览器和 CDN 的缓存时长可以分开控制:max-age 管浏览器,s-maxage 管共享缓存(CDN)。两者同时存在时,CDN 以 s-maxage 为准。想让 CDN 缓存更久、浏览器更短,就两个都写。
三个能显著提升体验的 CDN 配置:
- 自定义缓存键:默认缓存键包含完整 URL,含协议、 Host 、查询串。如果你的站点同时在两个域名上提供同样的图片,把 Host 从缓存键里去掉,可以让它们共用一份缓存,命中率直接翻倍。反之,如果
?utm_source这类参数进入了缓存键,同一份内容会被存成几十份。 - 陈旧服务(stale-while-revalidate):
max-age=3600, stale-while-revalidate=600表示过期后 10 分钟内先返回旧内容,同时后台异步回源刷新。用户不用等回源,源站抖动时也不至于报错。 - 对
/static/*与/api/*分开配策略:静态路径从缓存键里去掉查询串以拉满命中率;API 路径按实际用到的参数精确纳入,否则会出现返回错数据的缓存污染。
另外要注意:带 private 或 no-store 的响应 CDN 不会缓存,登录后页面和含用户标识的接口天然走源,不要指望 CDN 替你扛这部分压力。
Vary 头是个隐藏的缓存杀手
Vary 值,这条响应就完全不可缓存。Vary: Accept-Encoding、Vary: Accept 这类是允许的;自定义头(比如 Vary: X-User-Region)多半会让 CDN 直接放弃缓存。配完之后用 curl -I 看一眼响应头,确认真的命中了。发布时怎么刷新
HTTP 没有”清除已存缓存”的指令——服务器无法主动删除中间节点上已经存好的响应。所以刷新只能靠 CDN 提供的失效接口,并且要省着用。
- 带指纹的资源根本不需要刷新,发新版就是新 URL,旧缓存自然淘汰。
- 只对 HTML 与无指纹文件做失效,路径尽量精确,别动不动全站刷新。
- 把失效动作写进部署脚本,部署完成自动触发,不要靠人记得点。
- 频繁刷新是设计问题的信号:说明有资源本该加指纹却没有加。
五个常见坑
- 没加指纹就给长缓存:改了不生效,且无法修复,只能等 TTL 过期。
- HTML 加了
max-age:发版后用户看到旧页面,症状随机、难以复现。 - 查询串进了缓存键:命中率被
utm_*参数打散,源站压力不降反升。 - 带认证的资源想进共享缓存:Basic/Digest 认证下的资源不会被共享缓存存储,需要
public或s-maxage显式声明。 - 用
Clear-Site-Data当刷新手段:它只清浏览器缓存,对 CDN 无效,且会把该站点所有已存响应全清掉。
- 盘一遍资源:列出带指纹的构建产物、 HTML 入口、无指纹的上传文件三类,分别落到不同路径前缀。
- 在源站按路径下发响应头:构建产物一年加 immutable,HTML 用 no-cache,上传目录按更新频率给 1 小时到 1 天。
- 配 CDN 缓存键:静态路径去掉查询串,API 路径只保留真正影响返回的参数。
- 给 HTML 与动态内容开 stale-while-revalidate,先给 10 分钟观察命中率与回源量。
- 用
curl -I抽查若干路径,确认 Age 在涨、 X-Cache 命中,再把失效动作接进部署脚本。
和性能优化、 Core Web Vitals 的关系
缓存头解决的是”重复访问的等待时间”,它和Core Web Vitals 的三条线是互补关系:缓存让回头客更快,CWV 衡量的是首次加载的体感。两者都做才完整,更完整的优化项清单见 AI 生成网站性能优化。
上传目录里的图片建议先压缩再上 CDN,能直接把回源带宽和边缘存储都降下来,做法见 AI 建站图片素材处理。另外提醒一句:缓存命中率高不等于抗住了恶意流量,频率限制要单独配,参考 AI 建站怎么防爬与限流。
no-cache 和 no-store 到底用哪个
改了 CSS 但用户看到的还是旧的怎么办
immutable 加了有什么实际作用
缓存时间设多久合适
小结
缓存配置没有玄学,就三步:给静态资源加指纹、按路径下发不同的响应头、把刷新接进部署流程。做好这三步,CDN 才会真正替你扛住流量;做不好,它只是一层昂贵的转发。站点要迁移或换 CDN 时,记得按 服务器迁移的灰度流程走,缓存配置差异是最容易在切换后才暴露的问题之一。










评论 (0)