HTTP 完全指南(二):缓存机制深度详解 引言上一篇我们学习了 HTTP 的报文结构。今天要讲的是 HTTP 中最实用的优化手段——缓存。为什么网页第二次打开比第一次快为什么浏览器有时显示从缓存加载为什么改完代码刷新页面还是旧内容、需要强制刷新这些都是 HTTP 缓存在起作用。理解缓存机制不仅能帮你写出加载更快的网页还能让你在遇到缓存问题时快速定位原因而不是盲目地清缓存、重启浏览器。第一部分缓存的分类HTTP 缓存分为两大类强缓存和协商缓存。缓存类型是否发请求状态码控制字段强缓存❌ 不发200 (from disk cache)Expires/Cache-Control协商缓存✅ 发304 Not ModifiedLast-Modified/ETag第二部分强缓存强缓存 在缓存有效期内直接使用本地缓存不向服务器发请求。一、ExpiresHTTP/1.0响应头 Expires: Mon, 02 Jun 2025 10:00:00 GMT含义在Mon, 02 Jun 2025 10:00:00 GMT之前直接使用缓存不请求服务器。致命缺陷Expires 使用绝对时间。如果客户端和服务器时间不同步比如客户端时间比服务器快 5 分钟缓存可能提前失效或过期后仍被使用。二、Cache-ControlHTTP/1.1推荐响应头 Cache-Control: max-age3600含义从请求时刻算起缓存有效3600 秒1 小时。这是相对时间完美解决了 Expires 的时间同步问题。Cache-Control 常用指令指令含义方向max-age秒缓存有效期相对时间响应public任何缓存都可以存储浏览器CDN响应private只能浏览器缓存不能 CDN 缓存响应no-cache可以缓存但每次必须验证请求/响应no-store完全不缓存请求/响应must-revalidate过期后必须重新验证响应三、Expires 和 Cache-Control 同时存在Expires: Mon, 02 Jun 2025 10:00:00 GMT Cache-Control: max-age3600Cache-Control 优先级高于 Expires。两个都写是为了兼容老版本客户端。四、强缓存命中时发生了什么第三部分协商缓存协商缓存 缓存过期了或设置了 no-cache浏览器向服务器验证我的缓存还能用吗服务器根据资源的标识判断没变 → 返回304 Not Modified浏览器继续用缓存变了 → 返回200 OK 新内容一、Last-Modified / If-Modified-Since基于时间Last-Modified 的缺陷秒级精度不够如果文件在 1 秒内被修改两次Last-Modified 分辨不出内容不变但修改时间变了文件被重新保存没改内容Last-Modified 也变了不适用于动态资源动态生成的页面没有最后修改时间二、ETag / If-None-Match基于内容推荐ETag 如何生成对文件内容做MD5或SHA-1哈希Nginx 默认用文件修改时间 文件大小生成动态资源可以用业务数据的版本号ETag 解决了 Last-Modified 的所有缺陷问题Last-ModifiedETag秒级精度不够❌✅ 内容变了就变只改时间不改内容❌ 会误判✅ 内容不变 ETag 不变动态资源❌ 没有修改时间✅ 可自定义生成三、Last-Modified 和 ETag 同时存在响应头 Last-Modified: Mon, 02 Jun 2024 10:00:00 GMT ETag: abc123ETag 优先级更高。浏览器先验证 ETag再验证 Last-Modified。两个都写也是为了兼容性。第四部分浏览器行为一、不同操作对缓存的影响操作强缓存协商缓存地址栏回车、链接跳转✅ 生效✅ 生效F5 刷新❌ 跳过✅ 生效Ctrl F5 强制刷新❌ 跳过❌ 跳过二、浏览器缓存位置第五部分缓存策略实战一、不同类型的资源如何设置缓存资源类型推荐策略Cache-Control原因首页 HTMLno-cachemax-age0, must-revalidate每次验证确保最新CSS/JS带版本号强缓存max-age31536000, immutable一年不过期图片/字体强缓存max-age259200030 天API 接口no-store或协商缓存no-cache数据实时性高用户头像短强缓存max-age3005 分钟二、为什么 CSS/JS 文件名要带哈希!-- 老方式有缓存问题 -- link relstylesheet href/style.css !-- 现代方式带版本哈希 -- link relstylesheet href/style.a3f2b1c.css第六部分完整示例# 服务器响应头配置示例Nginx # HTML不缓存每次验证 location / { add_header Cache-Control no-cache, must-revalidate; } # 带哈希的静态文件长期缓存 location ~* \.[a-f0-9]{8}\.(css|js)$ { add_header Cache-Control max-age31536000, immutable; } # 图片中期缓存 location ~* \.(png|jpg|gif|svg|ico)$ { add_header Cache-Control max-age2592000; }第七部分面试题1. Q强缓存和协商缓存有什么区别A强缓存命中时不向服务器发请求直接用本地缓存200 from cache。协商缓存每次向服务器发请求验证304 时用缓存200 时重新下载。2. QExpires 和 Cache-Control 的区别AExpires 是 HTTP/1.0 的用绝对时间服务器时间客户端时间不准会出问题。Cache-Control 是 HTTP/1.1 的用相对时间max-age优先级更高。3. Qno-cache 和 no-store 的区别Ano-cache 可以缓存但每次必须向服务器验证。no-store 完全不缓存。4. QLast-Modified 和 ETag 的区别ALast-Modified 基于时间秒级精度时间变了内容不变也会误判。ETag 基于内容哈希更准确优先级更高。5. QF5 刷新和 CtrlF5 强制刷新的区别AF5 跳过强缓存走协商缓存带上 Cache-Control: max-age0。CtrlF5 跳过所有缓存重新下载带上 Cache-Control: no-cache Pragma: no-cache。6. Q为什么静态资源文件名要带哈希A配合强缓存使用。文件内容变了 → 哈希变了 → 文件名变了 → 浏览器认为是新文件 → 不走缓存直接下载。没变 → 文件名不变 → 走缓存。总结一、核心对比对比强缓存协商缓存发请求❌ 不发✅ 发状态码200 (from cache)304控制字段Cache-Control / ExpiresETag / Last-Modified优先级先判断强缓存失效后才走速度最快0ms 网络较快有请求但无响应体二、一句话记忆HTTP 缓存分两级强缓存用 Cache-Control/Expires 控制有效期有效期内根本不发请求过期后用 ETag/Last-Modified 向服务器协商验证304 就继续用旧的、200 就下载新的。no-cache 不是不缓存、no-store 才是不缓存。