HTML缓存核心是Cache-Control响应头,但浏览器对/index.html等入口文件默认谨慎处理,优先协商缓存而非强缓存;真正有效策略是生成带哈希的HTML文件(如index.a1b2c3.html)并配public, immutable, max-age=31536000,或对无用户态页面设public, max-age=600且确保URL随内容变更。

Cache-Control 响应头是控制 HTML 缓存的核心开关
HTML 文件默认几乎不被浏览器缓存,哪怕你写了
Cache-Control: public, max-age=3600</p> <p>原因很简单:浏览器对 HTML 入口文件极其谨慎。它会优先走协商缓存(比如带 <code>If-Modified-Since或
If-None-Match),甚至在某些版本 Chrome 中,即使响应头写了 max-age=86400,刷新页面时仍可能发请求并收到 304 —— 这不是 bug,是设计使然。
真正能落地的策略是:把 HTML 本身“静态化”或“去动态化”。例如:
- 用构建工具(如 Vite、Webpack)生成带哈希后缀的 HTML(
index.a1b2c3.html),再通过 Nginx 重写所有请求到该文件,并配Cache-Control: public, immutable, max-age=31536000 - 后端模板渲染时,若页面无用户态依赖(如登录态、实时价格),可主动输出
Cache-Control: public, max-age=600,并确保每次内容变更都触发资源 URL 变更(靠版本号或时间戳) - 避免在 HTML 中内联动态脚本(如
<script>var user = {...}</script>),这类内容会让整个 HTML 失去缓存价值
不要依赖 Expires 或 Pragma 控制 HTML 缓存
Expires 是 HTTP/1.0 遗留字段,依赖客户端和服务端时钟同步,误差超过几分钟就可能导致缓存失效或误命中;Pragma: no-cache 在现代浏览器中已被 Cache-Control 完全覆盖,且对 HTML 几乎无效——它只影响请求阶段,不改变响应缓存行为。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 彻底删掉 HTML 响应里的
Expires和Pragma字段,只保留Cache-Control - 如果必须支持老旧代理,加
Cache-Control: public, max-age=XXX即可,s-maxage可额外控制 CDN 层缓存,但对浏览器无影响 - 用
curl -I https://yoursite.com/index.html检查实际返回头,确认没有被中间层(如 CDN、WAF)覆写掉你的Cache-Control
HTML 缓存失效比 JS/CSS 更难处理
JS 和 CSS 可以靠文件名哈希实现“内容不变则 URL 不变”,天然支持长期缓存;但 HTML 是入口,它的 URL 通常是固定的(/ 或 /index.html),一旦缓存太久,用户就看不到新功能或修复。
所以关键不是“缓存多久”,而是“怎么让缓存知道该失效”。常见踩坑点:
- 前端部署后清 CDN 缓存,但忘了告诉浏览器:用户还在用旧 HTML,里面引用的 JS 文件名虽变了,但旧 HTML 已缓存,导致白屏或报错
- 用
Cache-Control: no-cache以为能强制校验,结果每次访问都发请求,服务器压力没降,还多了大量304流量 - 在 HTML 中硬编码资源路径(如
src="js/app.js"),而不是用构建工具注入带哈希的路径,导致 HTML 缓存后 JS 更新无法生效
推荐做法:HTML 缓存周期设为 5–10 分钟(max-age=300),配合服务端主动失效机制(如部署时向 CDN 发送 purge 请求,或用版本参数 /index.html?v=2.3.1)。
Service Worker 不是 HTML 缓存的替代方案
有人想用 Service Worker 拦截 HTML 请求并缓存,这在技术上可行,但风险极高:
- 首次加载仍需网络请求,离线时若 SW 脚本本身未缓存,整个页面不可用
- HTML 更新后,SW 需要重新注册、激活、清理旧缓存,逻辑复杂,容易卡在 waiting 状态
- 浏览器对导航请求(navigation request)的 SW 缓存行为有特殊限制,部分场景下会忽略缓存直接 fallback 到网络
真正降低服务器压力的 HTML 缓存,靠的是让浏览器**少发请求**,而不是让 JS 代码去接管请求。SW 更适合兜底离线场景,而非日常缓存优化主路径。
最常被忽略的一点:HTML 缓存效果好不好,不取决于你写了多少响应头,而取决于你有没有切断 HTML 和动态数据的耦合。只要它还依赖实时接口、用户 session、时间戳渲染,那再长的 max-age 也只是自我安慰。



















