强缓存和协商缓存是分阶段协作的缓存机制:浏览器优先执行强缓存(通过cache-control或expires控制,命中时直接返回200 from cache),过期后才发起协商缓存请求(携带if-none-match或if-modified-since,服务端返回304或200)。

强缓存和协商缓存不是二选一,而是分阶段协作:浏览器先走强缓存,过期后再走协商缓存。配合使用能兼顾加载速度和资源新鲜度。
强缓存负责“免请求”阶段
通过 Cache-Control: max-age=N(推荐)或 Expires 告诉浏览器在 N 秒内直接读本地缓存,完全不发网络请求。这个阶段状态码显示为 200 (from memory cache) 或 200 (from disk cache)。
- 静态资源如 JS、CSS、图片适合设较长 max-age(例如 1 年:
max-age=31536000) - HTML 文件通常设较短 max-age(如 60 秒),或直接设
no-cache跳过强缓存,直奔协商 - 避免用
Expires单独配置,它依赖服务器时间,且优先级低于 Cache-Control
协商缓存负责“确认更新”阶段
当强缓存过期,浏览器发起请求,但会带上上次响应中的标识字段,让服务端判断资源是否变更。若未变,返回 304 Not Modified,不传响应体;若变了,返回 200 + 新内容。
- 常用组合是 ETag + If-None-Match(更精准,支持哈希比对)或 Last-Modified + If-Modified-Since(基于时间戳,简单但粒度粗)
- 服务端需在强缓存响应头中同时带上
ETag或Last-Modified,否则协商流程无法触发 - 注意:即使设置了协商缓存,也必须确保请求头携带对应字段(浏览器自动加,无需 JS 手动干预)
实际项目中的典型配置示例
以一个 JS 文件为例,服务端响应头可这样设置:
Cache-Control: public, max-age=3600 ETag: "xyz789" Last-Modified: Wed, 12 Aug 2026 09:30:00 GMT
效果是:
- 首次加载:发请求 → 返回 200 + 内容 + 上述头部
- 1 小时内再次访问:不发请求 → 直接读缓存 → 显示 200 (from cache)
- 1 小时后再次访问:发请求,带
If-None-Match: "xyz789"→ 服务端比对 ETag → 未变则返回 304
前端开发中需要注意的细节
JavaScript 本身不直接控制 HTTP 缓存策略,但会影响缓存行为:
- 动态引入资源(如
import('./module.js'))仍受 HTTP 缓存头约束,不是 JS 能绕开的 - 用
fetch或XMLHttpRequest请求接口时,若服务端返回了缓存头,浏览器一样会按规则执行,但默认多数 API 响应设了no-store或no-cache - 强制刷新(Ctrl+F5)会忽略强缓存,但协商缓存仍生效;普通 F5 会跳过强缓存,直接走协商
- 构建工具(如 Webpack/Vite)常给哈希命名的静态资源自动配 long-term cache(
max-age=31536000),这是强缓存最佳实践
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











