HTTP缓存

从问题:200(from cache)和304 有什么区别来看浏览器缓存。

为什么会用浏览器缓存:

通过网络获取资源既速度缓慢又代价高昂:下载过程可能需要在客户端和服务器之间进行多次往返,这会导致延迟处理,并可能会阻止网页内容呈现,还会致使访问者支付数据费用。所有的服务器响应都应指定一种缓存政策,以帮助客户端确定是否以及何时能够重用之前获取的响应。

客户端发出一个请求之后经历了什么?

浏览器发出的http请求绘首先被路由到浏览器的缓存,查看缓存中是否有可以用于实现的有效响应,如果有匹配的响应,可以直接从缓存中读取响应,这样就避免了网络延迟及传输产生的数据成本。

img

详细解释这个过程需要引入http的各个请求头

1、【Cache-Control:no-cache】和【Pragma:no-cache】—禁止缓存

首先判断是否禁止缓存,要是禁止缓存,需要直接向服务器发出请求。

禁止缓存:缓存中不得存储任何关于客户端请求和服务端响应的东西,每次由客户端发起的请求都会下载完整的内容。

2、有缓存的情况下,是否检查缓存是否过期

检查,需要查看max-age,如果没有该属性,查看Expires属性,没有的话查找Last-Modified信息。

《不检查的话直接是下面的步骤》

  • 过期:到服务器进行有效性的验证。即ETag

作用:

在响应过期之后,发起新的请求,但此时如果服务器资源未被更改,返回304 Not Modified响应,告诉浏览器缓存中的响应未被修改过,不必再次下载响应 。

1
2
3
4
5
在请求头中查找 if-None-Match字段,值为服务器上次返回的ETag的响应头的值

没有if-None-Match字段,查找if-Modified-Since字段,其值为服务器上次返回的Last-Modified响应头中的日期值。

有的话,到服务端进行验证,没有变化304,从缓存读取,有变化200。
  • 本地副本没有过期,直接从缓存中读取

缓存实践

1、缓存时间方面:

​ 【Expires / Cache-Control】

1
2
Expires使用的失效时间   http1.0
Cache-Control使用时间间隔 http1.1
1
2
3
4
5
6
Cache-Control http1.1中加入的新属性,它有以下常用参数:

- Public/Private 私有缓存/共有缓存
- no-cache:不建议使用本地缓存,但仍然会缓存到本地
- no-store:不会在客户端缓存任何数据
- max-age:比较特殊,是一个混合属性,替代了Expires的过期时间

2、有效性验证

​ 【Last-Modified/ETag】

都是通过某个标识符请求资源,如果资源没有变化,返回304(Not Modified)内容为空,节省了传输数据量。当资源发生变化后,返回和第一次请求时类似。保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。

Last-Modified使用文件最后修改作为文件标识值,它无法处理文件一秒内多次修改的情况,而且只要文件修改了哪怕文件实质内容没有修改,也会重新返回资源内容;ETag作为“被请求变量的实体值”,其完全可以解决Last-Modified头部的问题,但是其计算过程需要耗费服务器资源。

既生Last-Modified何生Etag?

你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

1
2
3
4
5
1.Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间

2.如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存

3.有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。

需要达成的目标

  • 在过期时间之前缓存可以用
  • 在文件有变化的时候客户端立即更新
  • 客户端时间不正确也可以正常工作
1
比较好的一个方案是使用Cache-Control,并且结合文件版本号。即在文件名中嵌入的一个“指纹”

img

将js与css等静态资源设置Cache-Control一年有效期,然后在文件名中加入了一串版本号信息。

那么在文件没有变动的时候,浏览器不用发起请求直接可以使用缓存文件;

而在文件有变化的时候,由于文件版本号的变更,导致文件名变化,请求的url变了,自然文件就更新了。

一些点:

  • Last-Modified 与 Etag的方式还是会请求服务器的,只不过返回的是304,对于304的请求F5是不会重新下载的。
  • Expires 与 Cache-Control max-age是直接从本地读取的,不需要请求服务器,他们的“返回状态”是200 OK(BFCache),这个时候F5是会重新下载的。

补充:

与缓存相关的重要的头字段

1、Cache-Control(重要策略):

1
Cache-Control包括:max-age / s-maxage/public/private/no-cache/no-store/must-revalidate等
  • max-age(单位为s)指定设置缓存最大的有效时间,定义的是时间长短。当浏览器向服务器发送请求后,在max-age这段时间里浏览器就不会再向服务器发送请求了。

max-age=2592000,也就是说缓存有效期为2592000秒(也就是30天)。于是在30天内都会使用这个版本的资源,即使服务器上的资源发生了变化,浏览器也不会得到通知。

max-age会覆盖掉Expires

  • s-maxage(单位为s)同max-age,只用于共享缓存(比如CDN缓存)

    当s-maxage=60时,在这60秒中,即使更新了CDN的内容,浏览器也不会进行请求

  • public 指定响应会被缓存,并且在多用户间共享

  • private 响应只作为私有的缓存(见下图),不能在用户间共享

  • no-cache 指定不缓存响应,表明资源不进行缓存

  • no-store 绝对禁止缓存,一看就知道如果用了这个命令当然就是不会进行缓存啦~每次请求资源都要从服务器重新获取。
    must-revalidate指定如果页面是过期的,则去服务器进行获取。

2、Expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点

3、Last-modified

服务器端文件的最后修改时间

4、ETag

根据实体内容生成一段hash字符串,标识资源的状态,由服务端产生。浏览器会将这串字符串传回服务器,验证资源是否已经修改,如果没有修改,过程如下:

img

回答开头的问题:

1
2
3
刷新后实际没有发送任何请求,因为 Cache-Control 定义的缓存时间段还没到期。在Chrome中即使没发送请求,但只要从本地的缓存中取,都会在Network面板显示一条状态为200且注明“from cache”的伪请求,其Response内容只是上一次回包留下的数据。

前面提到过,在Chrome中如果点击“刷新”按钮,Chrome会强制给所有资源加上“Cache-Control: max-age=0”的请求首部并向服务器发送验证请求的,而在文章开头的动图中,我们的确点击了“刷新”按钮,却不见浏览器发去新请求(并返回304)

HTTP缓存控制小结

HTTP缓存

HTTP 缓存

缓存策略 (CDN缓存。HTML5缓存)

各种优化