一、强缓存
不向服务器发送请求,直接从缓存中读取资源。
有两种方式:Expires和Cache-Control(若响应两者都有,则Cache-Control优于Expires)
no-cache:(Cache but revalidate)
A cache will send the request to the origin server for validation before releasing a cached copy.
每次有请求发出时,缓存会将此请求发到服务器(该请求与本地缓存有关,服务器校验请求中所描述的缓存是否过期,若未过期(返回缓存304),则缓存才使用本地缓存)
no-store:(No caching)
The cache should not store anything about the client request or server response. A request is sent to the server and a full response is downloaded each and every time.
缓存不得存储任何关于客户端请求和服务器响应的内容。每次由客户端发起的请求都会走完成的响应内容。
max-age:资源被缓存(保持新鲜)的最大时间
对于强缓存,chrome浏览器的状态码:
200 OK(from disk cache)或是200 OK (from memory cache)
例如:请求某个图片后,当浏览器再次访问这个图片时,发现有这个图片的缓存,且缓存没过期,所以就使用缓存。
当浏览器发现缓存过期后,缓存并不一定不能使用了比如文件虽然过了有效期,但内容并没有发生改变,还是可以用缓存数据。所以,这个时候需要与服务器协商,让服务器判断本地缓存是否还能使用。那么又怎么判断服务端文件有没有更新呢?主要有两种方式:
Last-Modified,If-Modified-since。
二、协商缓存
Once a resource is stored in a cache, it could theoretically be served by the cache forever. Caches have finite storage so items are periodically removed from storage. This process is called cache eviction. On the other side, some resources may change on the server so the cache should be updated. As HTTP is a client-server protocol, servers can’t contact caches and clients when a resource changes; they have to communicate an expiration time for the resource. Before this expiration time, the resource is fresh; after the expiration time, the resource is stale. Eviction algorithms often privilege fresh resources over stale resources. Note that a stale resource is not evicted or ignored; when the cache receives a request for a stale resource, it forwards this request with a If-None-Match to check if it is in fact still fresh. If so, the server returns a 304 (Not Modified) header without sending the body of the requested resource, saving some bandwidth.
理论上来讲,当一个资源被缓存存储后,该资源应该可以被永久存储在缓存中。由于缓存只有有限的空间用于存储资源副本,所以缓存会定期地将一些副本删除,这个过程叫做缓存驱逐。另一方面,当服务器上面的资源进行了更新,那么缓存中的对应资源也应该被更新,由于HTTP是C/S模式的协议,服务器更新一个资源时,不可能直接通知客户端更新缓存,所以双方必须为该资源约定一个过期时间,在该过期时间之前,该资源(缓存副本)就是新鲜的,当过了过期时间后,该资源(缓存副本)则变为陈旧的。驱逐算法用于将陈旧的资源(缓存副本)替换为新鲜的,注意,一个陈旧的资源(缓存副本)是不会直接被清除或忽略的,当客户端发起一个请求时,缓存检索到已有一个对应的陈旧资源(缓存副本),则缓存会先将此请求附加一个If-None-Match头,然后发给目标服务器,以此来检查该资源副本是否是依然还是算新鲜的,若服务器返回了 304 (Not Modified)(该响应不会有带有实体信息),则表示此资源副本是新鲜的,这样一来,可以节省一些带宽。若服务器通过 If-None-Match 判断后发现已过期,那么会带有该资源的实体内容返回
Etag、If-None-Match
- 浏览器第一次向服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag字段;
- 浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match,这个值就是上一次请求时返回的ETag的值;
- 服务器再次收到资源请求时,再根据资源生成一个新的ETag,与浏览器传过来If-None-Match比较,如果这两个值相同,则说明资源没有变化,返回304 Not Modified, 浏览器从缓存中加载资源,否则返回200 资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化
Last-Modified、If-Modified-Since:
浏览器第一次向服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Last-Modified字段,表示该资源在服务器上的最后修改时间;
浏览器再次向服务器请求这个资源时,在request的header上加上If-Modified-Since字段,这个值就是上一次请求时返回的Last-Modified的值;
服务器收到资源请求时,比较If-Modified-Since字段值和被请求资源的最后修改时间,若资源最后修改时间较旧,则说明文件没有修改,返回304 Not Modified, 浏览器从缓存中加载资源;若不相同,说明文件被更新,浏览器直接从服务器加载资源, 返回200;
重新加载资源时更新Last-Modified Header
为什么有了Last-Modified,还要用Etag呢?
一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
某些服务器不能精确的得到文件的最后修改时间。
对于上述情景,利用ETag能够更加准确的控制缓存,因为ETag是服务器自动生成的资源在服务器端的唯一标识符,资源每次变动,都会生成新的ETag值。Last-Modified与ETag是可以一起使用的,但服务器会优先验证ETag。
响应中Age与Date
- Date:原服务器第一次响应的时间
- Age:HTTP/1.1 uses the Age response-header to convey the estimated age of the response message when obtained from a cache. The Age field value is the cache’s estimate of the amount of time since the response was generated or revalidated by the origin server.
That means the presence of the header field Age: 0 means that the received response was sent by an intermediate cache and is only zero seconds old. So it was probably just fetched from the origin server before sending it to the client.
从上面可以看出,Age表示中间环节(CDN或缓存服务器)从服务器拿到数据之后的时间。如果是客户端看到是0秒,代表是从服务器拿到的最新的数据。
三、将强缓存和协商缓存放在一张图中
参考:https://segmentfault.com/a/1190000016872406
https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching