应使用文件内容哈希生成唯一URL(如main.a8f3b2d.css),配合Nginx配置expires 1y和Cache-Control "public, immutable",并确保HTML自动引用新路径,同时清理CDN缓存。

能设长缓存,但必须配合 URL 变更——否则用户永远看不到新样式。 单独在响应头里写 Cache-Control: public, max-age=31536000 没用,浏览器认的是 URL,不是文件内容。
为什么加了 max-age 还是不更新 CSS?
因为浏览器看到 /css/app.css 这个 URL 没变,就直接复用本地缓存,根本不会发请求。哪怕你服务器上已替换成新内容,它也视而不见。这不是 bug,是 HTTP 缓存机制的正常行为。
常见错误现象:
- 构建后上线,部分用户样式“卡”在旧版,清缓存才恢复
- CDN 返回 304 或直接命中缓存,Network 面板里 CSS 请求状态是
memory cache或disk cache,size 显示from cache - 手动改了
style.css内容,但 HTML 中仍引用href="style.css",没触发重下载
三种版本号方式的实际效果对比
核心目标:让每次发布都生成一个**语义唯一、不可复用**的 URL。
立即学习“前端免费学习笔记(深入)”;
-
v=时间戳(如style.css?v=20260419):开发期方便,但破坏长期缓存;CI 构建若并行跑多次,可能产出相同时间戳,导致缓存误用 -
v=git commit hash(如style.css?v=abc1234):语义清晰,但 CI 环境若没.git目录或被清理过,git rev-parse会失败,构建中断 -
文件内容哈希(如main.a8f3b2d.css):最稳妥。内容一变,哈希必变,URL 必变;且无需外部依赖,Webpack/Vite 默认支持
注意:query string 在 Cloudflare 免费版、部分企业 CDN 上默认不参与缓存键计算,即 style.css?v=1 和 style.css?v=2 被当成同一个 URL 缓存——这会让版本参数完全失效。
服务端配置 Cache-Control 的关键细节
只配前端构建不够,服务端响应头必须对得上。否则浏览器或 CDN 会按自己理解的策略缓存。
- 对带哈希的 CSS 文件(如
main.a8f3b2d.css),Nginx 推荐配置:location ~* \.css$ {<br> expires 1y;<br> add_header Cache-Control "public, immutable";<br>}immutable是关键:告诉浏览器“这个 URL 永远不会变内容”,可跳过后续验证请求(省掉一次 If-None-Match) - 千万别对所有 CSS 统一配
no-cache:它强制每次验证,网络 RTT 拖慢首屏,且 CDN 可能仍返回 304,没解决实质问题 - 避免
no-store:CDN 会彻底绕过缓存,失去边缘节点加速意义,尤其对高并发静态资源场景是性能灾难
构建工具与 HTML 引用必须联动
光让 Webpack 输出 main.a8f3b2d.css 不够,HTML 中的 <link href="main.css"> 得自动替换成新路径,否则 404。
- Webpack:靠
MiniCssExtractPlugin+HtmlWebpackPlugin,确保filename: '[name].[contenthash:8].css'和 HTML 插件开启inject: true - Vite:默认启用
build.rollupOptions.output.assetFileNames哈希,且index.html中写<link rel="stylesheet" href="/style.css">即可,Vite 会在构建时自动映射到带 hash 的真实文件 - 手写 HTML 或 CMS 管理页面时,切勿硬编码路径;应通过构建后注入变量、或服务端模板(如 EJS、Django)动态输出正确 URL
真正容易被忽略的是 CDN 缓存层级:它可能比你想象中多一层(比如源站 Nginx → 边缘节点 → 浏览器),且不报错、不打日志,只默默返回旧内容。上线后务必调用 CDN 的 purge 接口,或确保其缓存键包含完整 URL(含哈希)。


















