http缓存机制
背景
http缓存指的是: 当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。
常见的http缓存只能缓存get请求响应的资源,对于其他类型的响应则无能为力,所以后续说的请求缓存都是指GET请求。
http缓存都是从第二次请求开始的。第一次请求资源时,服务器返回资源,并在respone header头中回传资源的缓存参数;第二次请求时,浏览器判断这些请求参数,命中强缓存就直接200,否则就把请求参数加到request header头中传给服务器,看是否命中协商缓存,命中则返回304,否则服务器会返回新的资源。
强缓存
浏览器在请求某一资源时,会先获取该资源缓存的header信息,判断是否命中强缓存(cache-control和expires信息),若命中直接从缓存中获取资源信息,包括缓存header信息;本次请求根本就不会与服务器进行通信。
状态码为200
Expires
Expires是http1.0的规范,它的值是一个绝对时间的GMT格式的时间字符串,这个时间代表这这个资源的失效时间,只要发送请求时间是在Expires之前,那么本地缓存始终有效,则在缓存中读取数据。失效的时间是一个绝对时间,当服务器与客户端时间偏差较大时,会导致缓存混乱。如果同时出现Cache-Control:max-age和Expires,那么max-age优先级更高。
Cache-Control
Cache-Control是在http1.1中出现的,主要是利用该字段的max-age值来进行判断,它是一个相对时间,例如Cache-Control:max-age=3600,代表着资源的有效期是3600秒。cache-control除了该字段外,还有下面几个比较常用的设置值:
类型 | 描述 |
---|---|
public | 客户端和代理服务器都可以缓存该资源;客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,status code:200 ,如果用户做了刷新操作,就向服务器发起http请求 |
private | 只让客户端可以缓存该资源,代理服务器不缓存;客户端在xxx秒内直接读取缓存,status code:200 |
immutable | 客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,status code:200 ,即使用户做了刷新操作,也不向服务器发起http请求 |
no-cache | 跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端 |
no-store | 不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了 |
协商缓存
如果没有命中强缓存,浏览器会发送请求到服务器,请求会携带第一次请求返回的有关缓存的header字段信息(Last-Modified/If-Modified-Since和Etag/If-None-Match),由服务器根据请求中的相关header信息来比对结果是否协商缓存命中;若命中,则服务器返回新的响应header信息更新缓存中的对应header信息,但是并不返回资源内容,它会告知浏览器可以直接从缓存获取;否则返回最新的资源内容。
状态码为304
last-modified
last-modified
记录资源最后修改的时间。启用强缓存后,请求资源之后的响应头会增加一个last-modified
字段,当再次请求该资源时,请求头中会带有if-modified-since
字段,值是之前返回的last-modified
的值。服务端会对比该字段和资源的最后修改时间,若一致则证明没有被修改,告知浏览器可直接使用缓存并返回 304;若不一致则直接返回修改后的资源,并修改last-modified
为新的值。
但 last-modified
有以下两个缺点:
- 只要编辑了,不管内容是否真的有改变,都会以这最后修改的时间作为判断依据,当成新资源返回,从而导致了没必要的请求响应,而这正是缓存本来的作用即避免没必要的请求。
- 时间的精确度只能到秒,如果在一秒内的修改是检测不到更新的,仍会告知浏览器使用旧的缓存。
etag
为了解决 last-modified
上述问题,有了 etag
。 etag
会基于资源的内容编码生成一串唯一的标识字符串,只要内容不同,就会生成不同的 etag
。启用 etag
之后,请求资源后的响应返回会增加一个 etag
字段,当再次请求该资源时,请求头会带有if-no-match
字段,值是之前返回的etag
值,服务端会根据该资源当前的内容生成对应的标识字符串和该字段进行对比,若一致则代表未改变可直接使用本地缓存并返回 304;若不一致则返回新的资源(状态码200)并修改返回的 etag
字段为新的值。
可以看出 etag
比 last-modified
更加精准地感知了变化,所以 etag
优先级也更高。不过从上面也可以看出 etag
存在的问题,就是每次生成标识字符串会增加服务器的开销。所以要如何使用 last-modified
和 etag
还需要根据具体需求进行权衡。
机制
1、用户发起了一个http
请求后,浏览器显示通过强缓存机制检测浏览器中该资源是否有缓存,有则直接返回资源,状态码为200,并且不会请求服务器。
2、倘若强缓存失效后,浏览器会向服务器请求资源,服务器会根据浏览器带过来的请求头信息进行协商缓存的检测,若协商缓存有效则直接返回资源,状态码为304.
3、当协商缓存也失效后,服务器重新获取资源返回给浏览器,状态码为200。
如何设置缓存
node等服务端设置
res.setHeader('max-age': '3600 public')
res.setHeader(etag: '5c20abbd-e2e8')
res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT)
nginx设置
本文参考:
https://segmentfault.com/a/1190000015816331
https://www.jianshu.com/p/9c95db596df5
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。