浏览器缓存策略
2023-03-29·6min
type
Post
summary
status
Published
category
tags
slug
date
Mar 29, 2023
password
icon
浏览器的缓存策略是由HTTP报文中与缓存相关的缓存标识(头部字段)来决定的。
常见的缓存标识有:
- Expires: 响应头,代表该资源的绝对过期时间。
- Cache-Control:请求/响应头,缓存控制字段,精确控制缓存策略。
- if-Modified-Sice:请求头,资源最近修改时间,也就是服务端上一次响应报文中的Last-Modified,由浏览器告诉服务端。
- Last-Modified:响应头,资源最近修改时间,由服务端告诉浏览器。
- if-None-Match: 请求头,资源唯一标识,也就是服务端上一次响应报文中的Etag,由浏览器告诉服务端。
- Etag:响应头,资源唯一标识,由服务端告诉浏览器。
缓存过程分析
HTTP基于请求-应答模型,也就是浏览器发起请求,服务端响应请求。
在浏览器第一次发起请求时,会根据返回的响应报文中的缓存标识,来决定是否缓存响应结果。如果是则将响应结果与缓存标识一起存入浏览器缓存中。

由上图可以知道:
- 浏览器每次发起请求,都会先在浏览器缓存中查找是否有该请求的结果和缓存标识。
- 浏览器每次收到响应,如果响应报文允许缓存,就会将该响应结果与缓存标识存入浏览器缓存中。
以上两点就是浏览器缓存机制的关键,它确保了每个请求的缓存存入与读取。
根据是否需要向服务器重新发起HTTP请求,可以将缓存过程分为两个部分,分别是:
- 强缓存:向浏览器缓存查找该请求结果,如果有缓存结果且强缓存有效,则直接返回结果,不发起请求的过程。
- 协商缓存:强缓存失效后,浏览器携带协商缓存标识发起HTTP请求,由服务端根据协商缓存标识来决定是否使用缓存的过程。
强缓存
强缓存决定了缓存能否直接使用,如果可以,则不需要发起请求,直接从缓存中读取资源,且会返回200的状态码。
控制强缓存的字段分别是
Expires
和Cache-Control
,其中Cache-Control
优先级比Expires
高。Expires(HTTP/1.0)
表示该资源的绝对过期时间,如果没过期就可以直接使用,强缓存生效,不需要发起请求。
缺点是Expires受限于客户端时间,如果修改了客户端时间,可能会造成缓存失效。
为了弥补这种缺点,HTTP/1.1增加了
Cache-Control
Cache-Control(HTTP/1.1)
该属性值表示资源会在30秒后过期,需要再次请求。也就是说在30秒内如果再次发起该请求,则会直接使用缓存,强缓存生效。
它与Expires相比:
- Expires的时间值,是一个绝对值。
- Cache-Control中的max-age=600 ,是相对值,用来解决Expires受限于客户端时间的问题。
除了max-age之外,Cache-Control还有以下属性:

总结
Cache-Control 的优先级高于 Expires,为了兼容 HTTP/1.0 和 HTTP/1.1,实际项目中两个字段都会设置。
协商缓存
如果强缓存失效了,例如:
- 没有 Cache-Control 和 Expires
- Cache-Control 和 Expires 过期
- 设置了 no-cache 或 max-age = 0
如果出现了上述三种情况,强缓存失效,那就需要进行协商缓存。
浏览器需要携带协商缓存标识重新发起HTTP请求,来验证服务器资源是否有更新,会有两种结果:
- 有更新,返回200,更新缓存
- 无更新,返回304,更新浏览器缓存有效期
控制协商缓存的的字段分别是:
- Last-Modified 和 If-Modified-Since
- Etag 和 If-None-Match 其中Etag/If-None-Match的优先级比Last-Modified/If-Modified-Since高
Last-Modified 和 If-Modified-Since(HTTP/1.0)
- Last-Modified(响应头)
- If-Modified-Since(请求头)
Last-Modified表示该资源文件最后修改日期,If-Modified-Since 会将之前收到的 Last-Modified 的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来并更新缓存,否则返回 304 状态码,表示可以继续使用缓存内容,并更新缓存有效期。
但这种根据更改时间的方式存在一些缺陷,例如:
- 如果服务端在1s内多次更新了这个资源,因为时间是秒级,从时间来看是无法分辨出是否有更新的。
- 如果该资源是定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽。
Etag 和 If-None-Match(HTTP/1.1)
为了解决Last-Modified 和 If-Modified-Since存在的问题,HTTP/1.1新增了这组缓存标识。
- ETag(响应头)
- If-None-Match(请求头)
ETag类似资源指纹,是资源的唯一标识。当资源有变化时,Etag就会重新生成,If-None-Match 会将之前收到的 ETag 发送给服务器,询问该资源 ETag 是否变动,有变动的话就将新的资源发送回来并更新缓存,否则返回 304 状态码,表示可以继续使用缓存内容,并更新缓存有效期。
使用ETag就可以精确地识别资源的变动情况,就算是秒内的更新,也会让浏览器感知,能够更有效地利用缓存。
ETag的强弱
ETag同时支持强校验和弱校验。它们通过ETag标识符的开头是否存在“W/”来区分,如
强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个“W/”标记,只要求资源在语义上没有变化。
缓存位置
既然浏览器缓存了资源,那么资源会被缓存在哪呢?

根据上图浏览器控制台Network中的Size栏,可以看到有两个缓存位置:
- memory cache
- disk cache
memory cache(内存缓存)
内存缓存具有两个特点,分别是快速读取和时效性:
- 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。
- 时效性:一旦该进程关闭,则该进程的内存则会清空。
disk cache(硬盘缓存)
硬盘缓存是直接将缓存写入硬盘文件中,读取缓存需要对硬盘文件进行I/O操作,然后重新解析该缓存内容,读取速度比内存缓存慢。
资源的缓存位置
既然有两个缓存位置,那么哪些资源会被放在内存缓存?哪些资源会被放在硬盘缓存?
类型 | 说明 |
memory cache(内存缓存) | 存储需要频繁读取的文件,一般是脚本、字体、图片等资源 |
disk ceche(硬盘缓存) | 存储不需要频繁读取的文件,一般是非脚本资源,如css等 |
因为CSS文件加载一次就可以渲染出来,我们不会频繁读取它,所以它不适合缓存到内存中。但是js之类的脚本却随时可能会执行,如果脚本在磁盘当中,我们在执行脚本的时候需要从磁盘取到内存中来,这样I/O开销就很大了,有可能导致浏览器失去响应。
访问缓存优先级
访问缓存遵循以下原则:
- 先在内存缓存中查找,如果有且强缓存有效,直接加载。
- 如果内存缓存中不存在,则在硬盘缓存中查找,如果有且强缓存有效直接加载。
- 如果硬盘缓存中也没有,则进行网络请求。
- 将请求获取的资源和缓存标识存到硬盘和内存。
缓存方案
大多项目都使用以下缓存方案:
- HTML: 协商缓存;
- css、js、图片:强缓存,文件名带上hash。