HTTP 缓存核心目标:减少请求、降低带宽、提升响应速度、减轻服务端压力

整体分为两大块:强制缓存协商缓存,优先级:强制缓存 > 协商缓存。

一、整体流程总览

1.客户端发起请求,先查本地缓存

2. 强制缓存有效:不发任何网络请求,直接使用本地资源,状态码仍为 200(from disk/memory cache)

3. 强制缓存失效:客户端带上缓存标识,向服务端发起请求走协商缓存

  • 资源未更新:服务端返回 304 Not Modified,客户端继续用本地缓存
  • 资源已更新:服务端返回 200 + 新资源,客户端更新本地缓存

二、第一部分:强制缓存(完全不请求服务端)

依靠两个响应头控制:Cache-Control(HTTP/1.1,主流)Expires(HTTP/1.0,兼容)

优先级:Cache-Control > Expires

1. Expires

  • 格式:Expires: 格林威治时间
  • 含义:资源绝对过期时间,到达该时间则缓存失效。
  • 缺点:
    • 基于客户端本地时间,客户端时间不准会导致缓存判断出错
    • 只支持秒级精度,现在基本作为兼容兜底

2. Cache-Control

单位:,相对时间,不受客户端时间影响,功能最全。

常用取值:

取值 作用 适用场景
max-age=xxx 缓存有效时长(从首次请求开始算) 静态资源(图片、JS、CSS、HTML)
public 客户端、代理服务器(Nginx/CDN)都可缓存 通用静态资源
private 仅客户端浏览器缓存,代理不缓存 用户个性化资源、登录态相关页面
no-cache 不走强制缓存,强制走协商缓存 资源更新较频繁、需要实时校验
no-store 完全禁止缓存,不落地任何本地 / 代理缓存 支付、隐私、敏感接口
must-revalidate 缓存过期后必须校验,禁止使用过期缓存 对数据一致性要求高的资源

三、第二部分:协商缓存(必须发请求,服务端校验)

强制缓存过期后触发。客户端请求头带上资源标识,服务端对比判断是否更新。 分两组标识,同时存在时,优先校验 ETag

1. 第一组:Last-Modified / If-Modified-Since

  • 首次响应:服务端返回响应头 Last-Modified: 资源最后修改时间
  • 后续请求:浏览器自动带上请求头 If-Modified-Since: 上次拿到的最后修改时间
  • 服务端逻辑:
    • 时间一致 → 资源未变 → 返回 304
    • 时间不一致 → 资源更新 → 返回 200 + 新数据

缺陷:

  • 精度只有秒级
  • 文件内容没变、仅修改时间被改动,也会误判为更新,导致缓存失效

2. 第二组:ETag / If-None-Match(更优方案)

  • 首次响应:服务端返回资源唯一指纹(哈希值) ETag: "abc123xyz"
  • 后续请求:浏览器自动携带 If-None-Match: "abc123xyz"
  • 服务端逻辑:
    • 指纹一致 → 304
    • 指纹不一致 → 200 + 新资源

优势:

  • 基于内容哈希,精度更高,不受修改时间影响
  • 优先级高于 Last-Modified,生产环境一般两者搭配使用

四、缓存整体执行顺序

1.浏览器请求资源

2.检查本地缓存 → 解析 Cache-Control/max-ageExpires

  • 未过期:强制缓存命中,直接读本地,200 (cache)
  • 已过期:进入协商缓存

3.请求头带上 If-None-Match(ETag) + If-Modified-Since(Last-Modified) 发往服务端

4.服务端依次校验:先比对 ETag,ETag 一致再比对 Last-Modified

5.校验通过(资源无更新)→ 返回 304,浏览器沿用旧缓存

6.校验不通过(资源更新)→ 返回 200 + 新资源 + 新缓存头,浏览器更新本地缓存

五、易混淆字段辨析

1. no-cache vs no-store

  • no-cache不是不缓存,而是不使用强制缓存,每次都要去服务端协商
  • no-store彻底禁用所有缓存,请求、响应都不落地,敏感接口首选

2. 304 状态码特点

  • 只返回响应头,无响应体,节省带宽
  • 属于协商缓存命中,不是错误码
  • 304 不会减少请求次数,依然会发起网络请求,只是不返回实体数据;强制缓存才会真正减少请求

六、不同角色的缓存落地(Java 后端实战)

1. 静态资源缓存(前端 + Nginx 为主)

图片、JS、CSS、字体文件

# HTTP
Cache-Control: public, max-age=2592000  # 缓存30天

策略:长缓存 + 文件名加版本号 / 哈希,更新资源则改文件名,绕过旧缓存。

2. 动态接口 / 业务接口

  • 不希望缓存:Cache-Control: no-store(支付、个人信息)
  • 准实时、允许短缓存:Cache-Control: no-cache(走协商缓存)

3. Nginx 配置 HTTP 缓存(线上常用)

Nginx 可作为代理缓存层,统一拦截静态资源,减轻后端服务压力。 核心配置:proxy_cacheproxy_cache_validetag on

4. Java 代码层面控制

Spring Boot 项目可通过响应头手动设置缓存规则:

response.setHeader("Cache-Control", "max-age=3600, public");
response.setHeader("ETag", etagValue);

也可结合 Spring Cache 做应用层本地缓存,和 HTTP 缓存分层使用。

七、面试经典问题 + 标准回答

1. 强制缓存和协商缓存区别?

  • 强制缓存:不发网络请求,直接读本地,由 Cache-Control/Expires 控制
  • 协商缓存:必须发请求,服务端校验,由 ETag/Last-Modified 控制
  • 优先级:强制缓存 > 协商缓存

2. ETag 和 Last-Modified 区别?

  • Last-Modified:基于文件修改时间,秒级精度,受客户端时间影响,存在误判
  • ETag:基于资源内容哈希,精度高,优先被校验
  • 生产环境一般两者同时开启

3. 为什么有了 Last-Modified 还要 ETag?

解决秒级精度不足、文件内容不变但修改时间变动导致的缓存失效问题。

4. no-cache 和 no-store 区别?

  • no-cache:依旧缓存,但每次必须协商校验
  • no-store:完全禁止缓存,数据不落地

5. 如何手动让浏览器缓存失效?

  • 强制刷新(Ctrl+F5):忽略本地缓存,直接请求服务端
  • 普通刷新(F5):跳过强制缓存,直接走协商缓存
  • 修改资源 URL(加版本号、时间戳)

6. CDN 缓存和 HTTP 缓存关系?

CDN 属于代理节点缓存,遵循 HTTP Cache-Control 规则,public 资源会被 CDN 缓存,实现就近访问加速。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐