浏览器在加载资源时,根据请求头的
expires
和cache-control
判断是否命中强缓存,是则直接从缓存读取资源,不会发请求到服务器。如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过
last-modified
和etag
验证资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,依然是从缓存中读取资源如果前面两者都没有命中,直接从服务器加载资源
相同点
如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;
#### 不同点
强缓存不发请求到服务器
协商缓存会发请求到服务器。
强制缓存
Cache-Control: no-store
:没有缓存。缓存中不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。Cache-Control: no-cache
:缓存但每次都验证。带着相关验证字段将请求发送到服务器。服务器会验证所描述的缓存是否过期。若未过期(304 NOT MODIFIED),则使用本地缓存。私有和共有
Cache-Control: public
:可以被所有用户缓存(多用户共享),包括终端和CDN等中间代理服务器Cache-Control: private
:只能被终端浏览器缓存(而且是私有缓存),不允许中继缓存服务器进行缓存。过期
Cache-Control: max-age=<seconds>
:表示资源能够被缓存(保持新鲜)的最大时间。验证方式
Cache-Control: must-revalidate
:意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。
Pragma
头
Pragma
是HTTP/1.0标准中定义的一个header属性,请求中包含Pragma的效果跟在头信息中定义Cache-Control: no-cache相同,但是HTTP的响应头没有明确定义这个属性,所以它不能拿来完全替代HTTP/1.1中定义的Cache-control头。通常定义Pragma以向后兼容基于HTTP/1.0的客户端。
协商缓存
用户点击刷新按钮时会开始缓存验证。如果缓存的响应头信息里含有"Cache-control: must-revalidate”的定义,在浏览的过程中也会触发缓存验证。另外,在浏览器偏好设置里设置Advanced->Cache为强制验证缓存也能达到相同的效果。
当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回强校验器或者弱校验器时才会进行验证。
强校验器 ETag If-None-Match
响应头:Etag
请求头:If-None-Match
If-None-Match: <etag_value>
If-None-Match: <etag_value>, <etag_value>, …
If-None-Match: *
If-None-Match
是一个条件式请求首部。对于 GETGET
和 HEAD
请求方法来说,当且仅当服务器上没有任何资源的 ETag
属性值与这个首部中列出的相匹配的时候,服务器端会才返回所请求的资源,响应码为 200
。对于其他方法来说,当且仅当最终确认没有已存在的资源的 ETag
属性值与这个首部中所列出的相匹配的时候,才会对请求进行相应的处理。
对于 GET
和 HEAD
方法来说,当验证失败的时候,服务器端必须返回响应码 304 (Not Modified,未改变)。对于能够引发服务器状态改变的方法,则返回 412 (Precondition Failed,前置条件失败)。
服务器端在生成状态码为 304 的响应的时候,必须同时生成以下会存在于对应的 200 响应中的首部:Cache-Control、Content-Location、Date、ETag、Expires 和 Vary 。
弱校验器
响应头:Last-Modified
请求头:If-Modified-Since
说他弱是因为只能精确到 1s。如果响应头里含有这个信息,客户端可以在后续的请求中If-Modified-Since
来验证缓存。
两者Etag
优先级更高。
选择Etag
通常是因为:
- 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
- 某些服务器不能精确的得到文件的最后修改时间。
如何选择合适的缓存
大致的顺序
- Cache-Control —— 请求服务器之前
- Expires —— 请求服务器之前
- If-None-Match (Etag) —— 请求服务器
- If-Modified-Since (Last-Modified) —— 请求服务器
协商缓存需要配合强缓存使用,如果不启用强缓存的话,协商缓存根本没有意义
大部分web服务器都默认开启协商缓存,而且是同时启用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】
但是下面的场景需要注意:
- 分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败;
- 分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样);
https://github.com/amandakelake/blog/issues/41