Http 缓存
Http 缓存
HTTP 缓存分为 2 种,一种是强缓存,另一种是协商缓存。主要作用是可以加快资源获取速度,提升用户体验,减少网络传输,缓解服务端的压力。
Http 强缓存
不需要发送请求到服务端,直接读取浏览器本地缓存,在 Chrome 的 Network 中显示的 HTTP 状态码是 200 ,在 Chrome 中,强缓存又分为 Disk Cache (存放在硬盘中)和 Memory Cache (存放在内存中),存放的位置是由浏览器控制的。是否强缓存由 Expires、Cache-Control 和 Pragma 3 个 Header 属性共同来控制。
Expires
Expires 的值是一个 HTTP 日期,在浏览器发起请求时,会根据系统时间和 Expires 的值进行比较,如果系统时间超过了 Expires 的值,缓存失效。由于和系统时间进行比较,所以当系统时间和服务器时间不一致的时候,会有缓存有效期不准的问题。Expires 的优先级在三个 Header 属性中是最低的。
Cache-Control
Cache-Control 是 HTTP/1.1 中新增的属性,在请求头和响应头中都可以使用,常用的属性值如有:
max-age:单位是秒,缓存时间计算的方式是距离发起的时间的秒数,超过间隔的秒数缓存失效no-cache:不使用强缓存,需要与服务器验证缓存是否新鲜no-store:禁止使用缓存(包括协商缓存),每次都向服务器请求最新的资源private:专用于个人的缓存,中间代理、CDN 等不能缓存此响应public:响应可以被中间代理、CDN 等缓存must-revalidate:在缓存过期前可以使用,过期后必须向服务器验证
Pragma
Pragma 只有一个属性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用强缓存,需要与服务器验证缓存是否新鲜,在 3 个头部属性中的优先级最高。
协商缓存
当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了 If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。
ETag/If-None-Match
Etag: 服务器响应请求时,通过此字段告诉浏览器当前资源在服务器生成的唯一标识(生成规则由服务器决定)
If-None-Match: 再次请求服务器时,浏览器的请求报文头部会包含此字段,后面的值为在缓存中获取的标识。服务器接收到次报文后发现 If-None-Match 则与被请求资源的唯一标识进行对比。
- 不同,说明资源被改动过,则响应整个资源内容,返回状态码 200。
- 相同,说明资源无心修改,则响应 header,浏览器直接从缓存中获取数据信息。返回状态码 304.
但是实际应用中由于 Etag 的计算是使用算法来得出的,而算法会占用服务端计算的资源,所有服务端的资源都是宝贵的,所以就很少使用 Etag 了。
Last-Modified/If-Modified-Since
Last-Modified: 服务器在响应请求时,会告诉浏览器资源的最后修改时间。
if-Modified-Since: 浏览器再次请求服务器的时候,请求头会包含此字段,后面跟着在缓存中获得的最后修改时间。服务端收到此请求头发现有 if-Modified-Since,则与被请求资源的最后修改时间进行对比,如果一致则返回 304 和响应报文头,浏览器只需要从缓存中获取信息即可。 从字面上看,就是说:从某个时间节点算起,是否文件被修改了
- 如果真的被修改:那么开始传输响应一个整体,服务器返回:200 OK
- 如果没有被修改:那么只需传输响应 header,服务器返回:304 Not Modified
if-Unmodified-Since: 从字面上看, 就是说: 从某个时间点算起, 是否文件没有被修改
- 如果没有被修改:则开始`继续'传送文件: 服务器返回: 200 OK
- 如果文件被修改:则不传输,服务器返回: 412 Precondition failed (预处理错误)
这两个的区别是一个是修改了才下载一个是没修改才下载。 Last-Modified 说好却也不是特别好,因为如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为 Last-Modified 时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)。为了解决这个问题,HTTP1.1 推出了 Etag。
典型应用场景
- 静态资源缓存:CSS、JS、图片等静态文件通过强缓存(
Cache-Control: max-age=31536000)实现长期缓存,减少服务器压力。 - API 响应缓存:对于不常变化的 API 数据,通过协商缓存(ETag)减少数据传输量,提升响应速度。
- CDN 缓存:结合 CDN 服务商实现边缘节点缓存,加速全球用户访问。
- 单页应用(SPA)部署:HTML 文件使用
no-cache确保更新及时,JS/CSS 文件使用文件名哈希 + 长期缓存。
最佳实践
- 静态资源设置长期强缓存:
Cache-Control: max-age=31536000, immutable,配合文件名哈希确保更新。 - 动态内容使用协商缓存:设置
ETag和Last-Modified,服务端返回 304 状态码避免重复传输。 - 敏感页面禁止缓存:设置
Cache-Control: no-store和Pragma: no-cache,防止敏感信息被缓存。 - 合理设置 Vary 头:当缓存内容依赖请求头(如 Accept-Encoding)时,设置
Vary头确保缓存正确匹配。
常见问题
强缓存和协商缓存的区别是什么?
强缓存(Cache-Control/Expires)直接由浏览器判断是否使用本地缓存,不发送请求;协商缓存(ETag/Last-Modified)需向服务器验证资源是否更新,未更新则返回 304。强缓存性能更优,但实时性较差。
为什么有时候清除缓存后还是看到旧内容?
可能是 CDN 节点或中间代理服务器仍缓存着旧内容。解决方案:1)CDN 控制台手动刷新;2)更改文件名或增加版本号参数;3)设置合理的 s-maxage 控制共享缓存时间。
Cache-Control 和 Expires 有什么区别?
Expires 是 HTTP/1.0 的产物,使用绝对时间,受客户端时间偏差影响;Cache-Control 是 HTTP/1.1 的产物,使用相对时间(max-age),更可靠。Cache-Control 优先级高于 Expires。