Cache-Control 设置了 max-age=3600,为什么浏览器还是发请求?

柯言 ☘︎ 阅读 15

我给静态资源加了 Cache-Control: max-age=3600,但每次刷新页面,浏览器还是会发请求到服务器,只是返回 304。不是应该直接用本地缓存、不发请求才对吗?

我用的是 Nginx,配置如下:

location ~* .(js|css|png|jpg|jpeg|gif|ico|svg)$ {
    expires 1h;
    add_header Cache-Control "public, max-age=3600";
}
我来解答 赞 2 收藏
二维码
手机扫码查看
1 条解答
司徒英洁
你这个配置其实没问题,但有个常见的误解:max-age=3600 确实会让浏览器在 1 小时内不重新验证,但前提是没有强制刷新(Ctrl + F5)或地址栏回车时带 If-Modified-Since/If-None-Match 头。

你看到的 304,说明浏览器其实发了条件请求(带 If-Modified-Since / If-None-Match),这通常发生在:

- 页面第一次加载时,浏览器缓存了资源和 Last-Modified / ETag;
- 后续刷新(不是硬刷新)时,浏览器会发一个带验证头的请求;
- 服务器发现资源没变,返回 304,告诉浏览器“用本地缓存吧”。

但按理说,如果 Cache-Control 里有 max-age 且没过期,浏览器应该直接走内存/磁盘缓存,连请求都不发才对。

你检查下这个细节:Nginx 的 expires 指令其实会自动加 Cache-Control 的 max-age,但你又手动加了一次,虽然不冲突,但容易混淆。

更重要的是——Nginx 的 expires 默认用的是 Cache-Control: max-age=3600,但如果你没配置 add_header 覆盖,它不会自动加 public,而你加了 public,没问题,但要注意:

- 如果你用了 add_header,那 Nginx 就不会再自动加默认的 ExpiresCache-Control,这是对的;
- 但关键点在于:浏览器是否真的收到了 Cache-Control: public, max-age=3600 这个头?

建议你用浏览器 DevTools 的 Network 标签,点开那个资源,看 Response Headers 里是不是真有这个头。有时候 Nginx 配置没生效(比如 location 匹配不到),或者被 upstream 覆盖了。

还有一个坑:如果你的资源 URL 里有查询参数(比如 /app.js?v=123),而 Nginx 配置里没处理 query string,可能缓存头没生效。不过你用的是正则匹配静态文件后缀,一般没问题。

最后说个更优雅的写法,把 expires 和 add_header 合起来,避免重复:

location ~* .(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1h;
add_header Cache-Control "public, max-age=3600, immutable";
}


加个 immutable 可以彻底防止浏览器在刷新时发条件请求(Chrome 87+ 支持),这样连 304 都不会有了,直接本地走缓存——当然,前提是资源 URL 加了 hash(比如 app.a1b2c3.js),否则改了文件用户拿不到最新版。

如果没加 hash,建议别加 immutable,不然更新会很痛苦。

你先看看 Network 里那个资源的响应头是不是真带了 Cache-Control,如果有了还发请求,大概率是浏览器策略(比如你手动刷新了),不是配置问题。
点赞 2
2026-02-25 20:00