You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nginx下启用ETag与Last-Modified长期缓存静态文件的安全性及更新问题

关于Nginx静态资源缓存与更新的问题解答

嘿,这个问题问到点子上了——很多刚上手缓存配置的开发者都会踩这个坑,我给你把逻辑捋得明明白白:

核心结论:默认情况下,客户端不会自动同步更新

你给CSS/JS设了1年的缓存时长(比如expires 1y),同时开了ETag和Last-Modified,但在缓存有效期内,浏览器会直接用本地缓存,完全不会主动去服务器校验文件是否更新。只有当用户手动强制刷新(比如Ctrl+F5),或者缓存过期后,浏览器才会带着If-None-Match(对应ETag)或If-Modified-Since(对应Last-Modified)头去请求服务器,这时候才会同步新文件。

也就是说,如果你不改文件名,服务器端修改文件后,大部分用户在1年内都看不到更新,除非他们手动刷新——这显然不是你想要的结果。

ETag和Last-Modified的真实作用

这两个响应头是用来做协商缓存校验的,但它们的生效前提是:浏览器愿意发起校验请求。如果你的Cache-Control设了很长的max-age,浏览器会认为“缓存还新鲜,没必要去服务器问”,这两个头就发挥不了自动更新的作用。

不想改文件名?调整缓存策略是关键

如果你坚持不修改文件名(虽然我还是更推荐用文件名哈希的方案,比如style.abc123.css,这是业界通用的最优解),可以通过调整Cache-Control头来兼顾缓存性能和更新及时性:

推荐的Nginx配置示例

location ~* \.(css|js)$ {
    # 开启ETag和Last-Modified(默认开启,明确写上更稳妥)
    etag on;
    if_modified_since on;

    # 设置缓存控制:优先用本地缓存,同时后台异步校验更新
    add_header Cache-Control "public, max-age=86400, stale-while-revalidate=31536000";
    # 对应1年的过期时间,和max-age配合使用
    expires 1y;
}

这里的stale-while-revalidate是关键:

  • 浏览器在max-age(这里是1天)内直接用本地缓存,不发请求,保证性能;
  • 超过1天但在stale-while-revalidate(1年)的有效期内,浏览器会在后台异步向服务器发起校验请求;
  • 如果服务器返回文件已更新,下次用户访问就会用新文件;如果没更新,继续用旧缓存。

容易遗漏的配置点

  • 不要只依赖expires指令:expires 1y会自动设置Cache-Control: max-age=31536000,但没有stale-while-revalidate的话,浏览器不会主动校验;
  • 确保ETag和Last-Modified没有被禁用:Nginx默认开启这两个功能,但有些自定义配置可能会通过etag off或if_modified_since off关掉,一定要检查;
  • CDN层的缓存同步:如果你的站点用了CDN,必须在CDN后台也配置对应的协商缓存策略,不然CDN会一直缓存旧文件,客户端根本碰不到你的源服务器。

最后再啰嗦一句

虽然stale-while-revalidate能解决问题,但文件名哈希(内容哈希)才是静态资源缓存的最优解——每次文件内容变化,文件名就变,浏览器会直接请求新文件,完全没有缓存更新的顾虑。如果可以的话,建议你考虑这个方案。

内容的提问来源于stack exchange,提问作者user6188497

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:37:06