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

UX玉萱 阅读 39

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

我试过在 Nginx 里配置:

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

但 Chrome DevTools 的 Network 面板里,每次刷新都看到请求发出,状态是 304 Not Modified。这算没生效吗?

我来解答 赞 12 收藏
二维码
手机扫码查看
2 条解答
东方伊糖
Cache-Control: max-age=3600 设置的是资源的有效期为一小时,但这并不意味着浏览器完全不会向服务器发送请求。当用户刷新页面时,浏览器会发送一个条件性请求(如 If-Modified-Since 或 If-None-Match),去检查资源是否被修改过。如果资源没有变化,服务器会返回 304 Not Modified,浏览器就会继续使用缓存的资源。

你配置的 Nginx 设置看起来是对的,复制过去试试:
location ~* .(js|css|png|jpg)$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
}


注意检查其他可能覆盖这个设置的地方,比如响应头中是否有其他 Cache-Control 指令或者 ETag 和 Last-Modified 头信息影响了缓存策略。如果问题依旧存在,可以尝试清除浏览器缓存后再测试。
点赞
2026-03-24 11:26
慕容蓝月
这个问题的关键在于浏览器刷新行为的特殊性。当你手动刷新页面(按F5或刷新按钮),大多数浏览器会默认带上 Cache-Control: max-age=0 的请求头,强制检查资源是否有更新。

304状态码其实说明缓存机制是生效的 - 服务器确认资源没变,所以没有返回完整内容。但这不是最优状态,我们想要的是完全不发请求(直接从内存/磁盘读取)。

要解决这个问题,需要两方面的调整:

1. 确保服务器正确配置缓存头:
location ~* .(js|css|png|jpg)$ {
# 设置过期时间(同时会生成对应的Cache-Control)
expires 1h;

# 确保ETag被移除(避免304检查)
etag off;

# 更完整的Cache-Control设置
add_header Cache-Control "public, max-age=3600, immutable";
}


2. 理解不同刷新方式的区别:
- 普通刷新(F5):会发送条件请求(可能返回304)
- 强制刷新(Ctrl+F5):完全忽略缓存,返回200
- 地址栏回车/正常导航:会优先使用缓存

我建议你测试时用地址栏回车而不是刷新按钮,这样更接近真实用户行为。另外加上 immutable 参数可以告诉浏览器:在max-age期间资源永远不会变,连条件请求都不用发。

补充一个冷知识:Chrome在开发者工具打开时,默认行为会更倾向于发送请求验证缓存,这是为了方便调试。你可以:
1. 关闭DevTools再测试
2. 或者右键刷新按钮选择"正常重新加载"

最后提醒下,ETag和Last-Modified这类验证机制会和强缓存产生冲突。既然我们用了max-age,最好直接禁用ETag,就像上面配置那样。
点赞 1
2026-03-05 11:05