静态资源必须用 content hash 重命名并配置 Cache-Control: public, max-age=31536000, immutable,否则浏览器复用旧缓存;HTML 则需 no-cache + ETag 协商,确保及时更新。

静态资源必须用 content hash 重命名
只设 Cache-Control: max-age=31536000 不会生效——浏览器看到 app.js 这个 URL 没变,就直接复用旧缓存,哪怕你已部署新代码。真正起作用的是 URL 变化本身。
构建时生成带内容哈希的文件名,例如:app.a1b2c3.js、style.f4e5d6.css。这样每次内容变更,文件名就不同,浏览器自然发起全新请求并缓存。
- Webpack:开
output.filename: "[name].[contenthash].js" - Vite:默认启用
build.rollupOptions.output.entryFileNames带[hash] - 注意:CSS 中的图片引用(如
url(logo.png))也要走哈希处理,否则 CSS 文件变了但图片 URL 没变,图片仍可能被错误复用
Cache-Control 要配全 public + max-age + immutable
三个参数缺一不可:
-
public:允许 CDN、代理等中间节点缓存,不加它,CDN 就不会存你的 JS/CSS -
max-age=31536000:1 年有效期,足够覆盖绝大多数静态资源生命周期 -
immutable:关键!告诉浏览器“这个 URL 对应的内容永远不会变”,跳过If-None-Match协商请求,连 ETag 都不发
Nginx 示例配置:
立即学习“前端免费学习笔记(深入)”;
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
location ~* \.(js|css|png|jpg|gif|svg)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
}别漏掉 add_header ——Nginx 默认不自动加 Cache-Control,只靠 expires 指令不够。
HTML 文件不能长期强缓存
HTML 是入口文件,必须能及时反映更新。给它设 max-age=3600 或更长,用户刷新页面时大概率看不到新版本,除非手动清缓存或强制刷新。
- 正确做法:设
Cache-Control: no-cache,让浏览器每次都发请求,靠ETag或Last-Modified协商验证 -
ETag优于Last-Modified:后者精度只有秒级,且构建工具常重写修改时间;ETag是基于内容生成的哈希(如"abc123"),只要 HTML 内容不变,就不会触发重传 - Nginx 默认开启
ETag,无需额外配置;Apache 需确认未禁用FileETag
开发阶段要警惕热更新和本地缓存干扰
Webpack Dev Server 默认返回 Cache-Control: no-store,但如果你在本地调试时手动加了缓存头,又没清浏览器磁盘缓存,就会出现「改了代码却没反应」的情况。
- 排查方法:打开 DevTools → Network → 找到对应资源 → 看 Response Headers 是否含
Cache-Control,再看 status 是200(新内容)、304(协商命中)还是直接从 memory/disk cache 加载 - F5 刷新会跳过强缓存但保留协商缓存逻辑,所以即使你配了 1 年缓存,F5 后仍可能出现一次 304 请求——这不是配置错,是浏览器规范行为
- 上线前务必确认:HTML 的响应头不含
max-age,而 JS/CSS/图片的响应头含public, max-age=31536000, immutable且文件名带哈希
最容易被忽略的一点:缓存是否生效,不取决于你写了什么配置,而取决于浏览器最终收到的响应头 + 对应资源的 URL 是否随内容变化。两者必须同时成立,缺一不可。


















