HTTP中的强缓存与协商缓存
HTTP中的强缓存与协商缓存
强缓存
在浏览器加载资源的时候,首先会根据请求头的
expires
和cache-control
判断是否命中强缓存策略,判断是否向远程服务器请求资源还是去本地获取缓存资源。
Expires
Expires
是http1.0
的规范,用于表示资源的过期时间的请求头字段,值是一个绝对时间,是由服务器端返回的。
在浏览器第一个请求资源时,服务器端的响应头会附上
Expires
这个响应字段,当浏览器在下一次请求这个资源时会根据上次的expires
字段是否使用缓存资源(当请求时间小于服务端返回的到期时间,直接使用缓存数据)
expires是根据本地时间来判断的,假设客户端和服务器时间不同,会导致缓存命中误差。
Cache-control
上面我们提到了
Expires
有个缺点,当客户端本地时间被修改时浏览器会直接向服务器请求新的资源,为了解决这个问题,在http1.1
规范中,提出了cache-control
字段,且这个字段优先级高于上面提到的Expires
,值是相对时间。
在
cache-control
中有常见的几个响应属性值,它们分别是
属性值 | 值 | 备注 |
---|---|---|
max-age | 3600 | 例如值为3600,表示(当前时间+3600秒)内不与服务器请求新的数据资源 |
s-maxage | 和max-age一样,但这个是设定代理服务器的缓存时间 | |
private | 内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存) | |
public | 所有内容都将被缓存(客户端和代理服务器都可缓存) | |
no-store | 不缓存任何数据 | |
no-cache | 储存在本地缓存区中,只是在与原始服务器进行新鲜度再验证之前,缓存不能将其提供给客户端使用 |
协商缓存
上面提到的强缓存都是由本地浏览器在确定是否使用缓存,当浏览器没有命中强缓存时就会向浏览器发送请求,验证协商缓存是否命中,如果缓存命中则返回304状态码,否则返回新的资源数据。
协商缓存(也叫对比缓存)是由服务器来确定资源是否可用,这将涉及到两组字段成对出现的,在浏览器第一次发出请求时会服务器返回的响应会带上字段(Last-Modified或者
Etag
),则后续请求则会带上对于的对应请求字段(if-modified-since
或者if-none-Match
),若响应头没有Last-Modified
或者Etag
,则请求头也不会有对应的字段。
Last-modified
表示本地文件最后修改时间,由服务器返回。if-modified-since
是浏览器在请求数据时带上的字段,值是上次服务器返回的Last-modified。ETag
是一个文件的唯一标识符,当资源发生变化时这个ETag
就会发生变化。弥补了上面last-modified
可能出现文件内容没有变化但是last-modified
发生了变化出现重新向服务器请求资源情况。这个值也是又服务器返回的。if-none-match
是浏览器请求数据时带上的字段,值是上次服务器返回的ETag
。
Etag/If-None-Match(标识符字符串) 与 Last-Modified/If-Modified-Since 不同的是,返回 304 时,ETag 还是会重新生成返回至浏览器,Etag的优先级高于Last-Modified
结合强缓存具体请求流程
- 当浏览器发起一个资源请求时,浏览器会先判断本地是否有缓存记录,如果没有会向浏览器请求新的资源,并记录服务器返回的
last-modified
。- 如果有缓存记录,先判断强缓存是否存在(
cache-control
优先于expires
,后面会说),如果强缓存的时间没有过期则返回本地缓存资源(状态码为200)- 如果强缓存失效了,客户端会发起请求进行协商缓存策略,首先服务器判断
Etag
标识符,如果客户端传来标识符和当前服务器上的标识符是一致的,则返回状态码304 not modified
(不会返回资源内容)- 如果没有
Etag
字段,服务器会对比客户端传过来的if-modified-match
,如果这两个值是一致的,此时响应头不会带有last-modified
字段(因为资源没有变化,last-modified
的值也不会有变化)。客户端304状态码之后读取本地缓存。如果last-modified
。- 如果
Etag
和服务器端上的不一致,重新获取新的资源,并进行协商缓存返回数据。
为什么需要ETag
它的出现主要是解决
last-modified
几个比较难以解决的问题
- 在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为这文件被改动了,从而重新请求
- 可能有些文件修改比较频繁,秒级以内修改的,
If-Modified-Since
能检查到的粒度是秒级的,使用Etag
就能够保证这种需求下客户端在 1 秒内能刷新多次。- 有些服务器不能精确获取文件的最后修改时间
状态码区别
- 200 请求成功,服务器返回全新的数据
- 200
from memory cache / from disk cache
本地强缓存还在有效期,直接使用本地缓存- 304 请求成功,走了协商缓存,服务器判定(
Etag
和Last-modified
)没有过期,告知浏览器使用缓存
from memory cache :是页面刷新的时候内存取的。
from disk cache :页面tab关闭后从磁盘取的。
缓存优先级
expires
和
cache-control如果同时存在时,
cache-control会覆盖
expires,
expires无效,无论是否过期,。即
Cache-control > expires
强缓存和协商缓存如果同时存在时,会去先对比强缓存是否还再有效期,如果强缓存生效则对比协商缓存,即
强缓存 > 协商缓存
协商缓存
Etag
和last-modified
同时存在时,会先比较Etag
,last-modified
无效,即Etag > last-modified
刷新影响
- 当ctrl+f5强制刷新时,从服务器加载,跳过强缓存和协商缓存。
- 当f5刷新时,跳过强缓存,但是会检查协商缓存
其他补充
- 协商缓存想要配合强缓存使用,如果不开启强缓存使用,协商缓存没有意义
- 大部分
web
服务器默认开启协商缓存,且是同时开启last-modified
和Etag
注意场景
- 分布式系统里
last-modified
需要保持一致,以免负载到不同的机器导致比对失败,从而返回新资源- 分布式系统尽量关闭掉
Etag
,因为每一台服务器生成的Etag
是不同的