浏览器缓存就是把已经请求过的web资源拷贝一份副本存储在浏览器中,第二次请求时会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。
浏览器对于缓存的处理是根据第一次请求资源时返回的响应头来确定的也就是Response Headers
强缓存
左图为已经有缓存数据的情况:缓存未失效,直接使用缓存数据
表现为: Status Code: 200 (from memory cache) 或 (from disk cache )
- 右图为缓存数据失效的情况:向源服务器请求数据,将数据和缓存规则存入缓存(一般为浏览器内存或磁盘中)
那么问题来了,浏览器怎么知道缓存是否失效呢?
根据响应头里的 Expires 和 Cache-Control 来判断
- Expires 是HTTTP1.0版本定义的缓存字段,值为服务器端返回的到期时间(绝对时间),如果客户端的时间与服务器端的时间有误差,会导致缓存命中的误差。
- Cache-Control 是HTTP1.1版本定义的缓存字段
值 | 意义 |
---|---|
private | 客户端可以缓存 |
public | 客户端和代理服务器可以缓存 |
max-age=xxx | 缓存在xxx秒后失效 |
no-cache | 需要使用协商缓存来验证缓存数据 |
no-store | 不缓存 |
Expires和Cache-Control同时存在时Cache-Control值为max-aga=xxxx 会直接忽略Expires 。 `
上图可以看到,从浏览器内存中返回的数据,并没有向源服务器返回数据
Cache-Control对应的是2592000s以后缓存失效,也就是30天内请求这个url都直接从浏览器缓存中得到数据。
Expires: 2018年8月22日07:23:08过期
按照之前说的 Expires会被忽略
协商缓存
- 左图为缓存未失效时,先向缓存得到Etag或者Last-Modified,然后请求服务器Request Headers会带上If-None-Match(值就是上一次请求返回的Etag 唯一标识) 或If-Modified-Since (上一次请求返回的资源最后修改时间), 服务器收到If-None-Match 与唯一标识对比,没有改变 或 If-Modified-Since 与资源最后修改时间对比,没有改变,即响应HTTP 304告诉浏览器使用浏览器缓存。浏览器直接从缓存里拿到数据。
- 右图之前的步骤同上,但If-None-Match 与资源唯一标识不同 或 If-Modified-Since小于资源最后修改时间,则表示缓存的资源已经失效,返回状态码 200 并且返回最新的数据,浏览器缓存数据。
通过上面的分析可以看出
协商缓存:是根据响应头中的Etag, Last-Modified以及请求头中的If-None-Match,If-Modified-Since 字段来判断是否过期。
缓存规则优先级及生效情况:
- 强缓存与协商缓存同时存在,如强缓存还在生效期,强缓存覆盖协商缓存;
- 强缓存的expires和cache-control同时存在,则cache-control覆盖;
- 协商缓存Etag和Last-Modified同时存在时,Etag覆盖Last-Modified;
服务器配置http缓存(nginx为例)
1 | location ~ .*\.(js|css)$ { |
以上配置可以对应请求的响应头
常见的HTTP请求头及响应头
请求头字段 | 说明 | 示例 |
---|---|---|
Accept | 可接受的响应内容类型 | Accept: application/json |
Accept-Encoding | 可接受的响应内容的编码方式 | Accept-Encoding: gzip, deflate |
Accept-Charset | 可接受的字符集 | Accept-Charset: utf-8 |
Accept-Language | 可接受的响应内容语言列表 | Accept-Language: zh-CN,zh;q=0.9 |
Authorization | 用于表示HTTP协议中需要认证资源的认证信息 | Authorization: Basic OSdjJGRpbjpvcGVuIANlc2SdDE== |
If-None-Match | 设置客户端ETag,如果和服务端接受请求生成的ETage相同,允许服务端返回304 Not Modified | If-None-Match: |
W/“4fd3169315a2307bef83eb3fce4d1382” Connection | 用来指定当前的请求/回复中的,是否使用缓存机制。 | Connection: keep-alive |
Content-Type | 请求体的MIME类型 (用于POST和PUT请求中) | Content-Type:application/json;charset=UTF-8 |
Date | 发送该消息的日期和时间(以RFC 7231中定义的”HTTP日期”格式来发送) | Date: Dec, 26 Dec 2015 17:30:00 GMT |
Referer | 表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接将浏览器带到了当前页面。Referer 其实是Referrer 这个单词,但RFC制作标准时给拼错了,后来也就将错就错使用Referer 了。 |
Referer: http://itbilu.com/nodejs |
User-Agent | 浏览器的身份标识字符串 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 |
Cookie | 由之前服务器通过Set-Cookie (见下文)设置的一个HTTP协议Cookie |
Cookie: $Version=1; Skin=new; |
响应头字段 | 说明 | 示例 |
---|---|---|
Access-Control-Allow-Origin | 指定哪些网站可以跨域源资源共享 |
Access-Control-Allow-Origin: * |
Age | 响应对象在代理缓存中存在的时间,以秒为单位 | Age: 12 |
Cache-Control | 通知从服务器到客户端内的所有缓存机制,表示它们是否可以缓存这个对象及缓存有效时间。其单位为秒 | Cache-Control: max-age=3600 |
Date | 此条消息被发送时的日期和时间(以RFC 7231中定义的”HTTP日期”格式来表示) | Date: Tue, 15 Nov 1994 08:12:31 GMT |
ETag | 对于某个资源的某个特定版本的一个标识符,通常是一个 消息散列 | ETag: “737060cd8c284d8af7ad3082f209582d” |
Expires | 指定一个日期/时间,超过该时间则认为此回应已经过期 | Expires: Thu, 01 Dec 1994 16:00:00 GMT |
Last-Modified | 所请求的对象的最后修改日期(按照 RFC 7231 中定义的“超文本传输协议日期”格式来表示) | Last-Modified: Dec, 26 Dec 2015 17:30:00 GMT |
Refresh | 用于重定向,或者当一个新的资源被创建时。默认会在5秒后刷新重定向 | Refresh: 5; url=http://itbilu.com |
Server | 服务器的名称 | Server: nginx/1.6.3 |
Set-Cookie | 设置HTTP cookie |
Set-Cookie: UserID=itbilu; Max-Age=3600; Version=1 |
Content-Encoding | 服务端能够发送压缩编码类型 | Content-Encoding: gzip |
Content-Type | 服务端发送的类型及采用的编码方式 | text/html; charset=GB2312 |