http cache | 一枝秋雨

http cache

强缓存

原理

当浏览器对某个资源的请求命中了强缓存时,返回的 HTTP 状态为 200,在 chrome 的开发者工具的 network 里面 size 会显示为 from cache,强缓存是利用 Expires 或者 Cache-Control 这两个 HTTP Response Header 实现的,它们都用来表示资源在客户端缓存的有效期。

配置强缓存

  • 通过代码的方式,在 Web 服务器返回的响应中添加 Expires 和 Cache-Control Header
  • 通过配置 Web 服务器的方式,让 Web 服务器在响应资源的时候统一添加 Expires 和 Cache-Control Header

总结

针对静态资源使用,动态资源需要慎用

协商缓存

原理

浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的 HTTP 状态为 304 并且会显示一个 Not Modified 的字符串。

配置

协商缓存是利用的是 [Last-Modified,If-Modified-Since] 和 [ETag、If-None-Match] 这两对 Header 来管理。

Last-Modified && If-Modified-Since

  1. 浏览器第一次向服务器请求一个资源时,服务器在返回这个资源的同时,在 Respone 的 Header 加上 Last-Modified 的 Header,这个 Header 表示这个资源在服务器上的最后修改时间
  2. 浏览器再次向服务器请求这个资源时,在 Request 的 Header 上加上 If-Modified-Since 的 Header,这个 Header 的值就是上一次请求时返回的 Last-Modified 的值
  3. 服务器再次收到资源请求时,根据浏览器传过来 If-Modified-Since 和资源在服务器上的最后修改时间判断资源是否有变化。
    如果没有变化则返回 304 Not Modified,但是不会返回资源内容。
    如果有变化,就正常返回资源内容。
    当服务器返回 304 Not Modified 的响应时,Response Header 中不会再添加 Last-Modified 的 Header,因为既然资源没有变化,那么 Last-Modified 也就不会改变
  4. 浏览器收到 304 的响应后,就会从缓存中加载资源
  5. 如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified Header 在重新加载的时候会被更新,下次请求时,If-Modified-Since 会启用上次返回的 Last-Modified 值

存在问题

[Last-Modified,If-Modified-Since] 都是根据服务器的时间返回的 Header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个 Header 配合起来管理协商缓存是非常可靠的,但是有时候服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。

ETag && If-None-Match

  1. 浏览器第一次向服务器请求一个资源时,服务器在返回这个资源的同时,在 Respone 的 Header 加上 ETag 的 Header,这个 Header 是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系,所以能很好的补充 Last-Modified 的问题
  2. 浏览器再次向服务器请求这个资源时,在 Request 的 Header 上加上 If-None-Match的Header,这个 Header 的值就是上一次请求时返回的 ETag 的值
  3. 服务器再次收到资源请求时,根据浏览器传过来 If-None-Match 和根据资源内容生成一个新的 ETag 进行对比。如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回 304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于 ETag 重新生成过,Response Header 中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化
  4. 浏览器收到 304 的响应后,就会从缓存中加载资源

总结

强缓存不发请求到服务器,所以有时候资源更新了浏览器还不知道,但是协商缓存会发请求到服务器,所以资源是否更新,服务器肯定知道。

如果不启用强缓存的话,协商缓存根本没有意义。

强缓存和协商缓存

  • 浏览器在加载资源时,先根据这个资源的 HTTP Header 判断它是否命中强缓存,强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。比如某个 CSS 文件,如果浏览器在加载它所在的网页时,这个 CSS 文件的缓存配置命中了强缓存,浏览器就直接从缓存中加载这个 CSS,连请求都不会发送到网页所在服务器
  • 当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的 HTTP Header 验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回,但是不会返回这个资源的内容,而是告诉客户端可以直接从缓存中加载这个资源,于是浏览器就又会从自己的缓存中去加载这个资源
  • 强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器
  • 当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据

浏览器缓存

浏览器缓存是将文件保存在客户端,在同一个会话过程中会检查缓存的副本是否足够新,在后退网页时,访问过的资源可以从浏览器缓存中拿出使用。通过减少服务器处理请求的数量,用户将获得更快的体验。

Expires

HTTP 1.0 提出的缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。是一个绝对时间,Expires = max-age + 请求时间,需要和 Last-modified 结合使用。

Cache-Control 的优先级更高。Expires 是 Web 服务器响应消息头字段,在响应 HTTP 请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。

过程

  • 浏览器第一次向服务器请求一个资源,服务器在返回这个资源的同时,在 respone 的 Header 加上 Expires 的 Header,如:
  • 浏览器在接收到这个资源后,会把这个资源连同所有 Response Header 一起缓存下来
  • 浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的 Expires 跟当前的请求时间比较,如果请求时间在 Expires 指定的时间之前,就能命中缓存,否则没有命中
  • 如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header 在重新加载的时候会被更新。

缺点

Expires 是较老的强缓存管理 Header,由于它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果

Cache-Control

HTTP 1.1 的时候,提出了一个新的 Header,就是 Cache-Control ,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示

过程

  • 浏览器第一次向服务器请求一个资源,服务器在返回这个资源的同时,在 Respone 的 Header 加上 Cache-Control 的 Header,如:
  • 浏览器在接收到这个资源后,会把这个资源连同所有 Response Header 一起缓存下来
  • 浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和 Cache-Control 设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则没有命中
  • 如果缓存没有命中,浏览器直接从服务器加载资源时,Cache-Control Header 在重新加载的时候会被更新

Cache-Control 描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,所以相比较 Expires,Cache-Control 的缓存管理更有效,安全一些。

属性

  • max-age

max-age(单位为s)设置缓存最大的有效时间,定义的是时间长短

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

  • s-maxage

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

比如,当 s-maxage=60 时,在这 60 秒中,即使更新了 CDN 的内容,浏览器也不会进行请求。也就是说 max-age 用于普通缓存,而 s-maxage 用于代理缓存。如果存在 s-maxage,则会覆盖掉 max-age 和 Expires Header。

  • public

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

  • private 

响应只作为私有的缓存,不能在用户间共享。如果要求 HTTP 认证,响应会自动设置为 private

  • no-cache

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

设置了 no-cache 之后并不代表浏览器不缓存,而是在缓存前要向服务器确认资源是否被更改。因此有的时候只设置 no-cache 防止缓存还是不够保险,还可以加上 private 指令,将过期时间设为过去的时间

  • no-store 

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

  • must-revalidate

must-revalidate 指定如果页面是过期的,则去服务器进行获取

Last-modified 

服务器端文件的最后修改时间,需要和 cache-control 共同使用,是检查服务器端资源是否更新的一种方式。当浏览器再次进行请求时,会向服务器传送 If-Modified-Since 报头,询问 Last-Modified 时间点之后资源是否被修改过。如果没有修改,则返回码为 304,使用缓存;如果修改过,则再次去服务器请求资源,返回码和首次请求相同为 200,资源为服务器最新资源。

ETag

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

使用 ETag 可以解决 Last-modified 存在的一些问题。

  • 某些服务器不能精确得到资源的最后修改时间,这样就无法通过最后修改时间判断资源是否更新 
  • 如果资源修改非常频繁,在秒以下的时间内进行修改,而 Last-modified 只能精确到秒 
  • 一些资源的最后修改时间改变了,但是内容没改变,使用ETag就认为资源还是没有修改的

缓存流程

Cache-Control 

 

浏览器刷新行为

刷新

点击刷新按钮或者按 F5,会触发这种行为

浏览器直接对本地的缓存文件过期,但是会带上If-Modifed-Since,If-None-Match(如果上一次 Response 带 Last-Modified, Etag)这就意味着服务器会对文件检查新鲜度,返回结果可能是 304,也有可能是 200

强制刷新

用户按 Ctrl+F5

浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前从来没有请求过,返回结果是 200

地址栏回车

浏览器发起请求,按照正常流程,本地检查是否过期,然后服务器检查新鲜度,最后返回内容

点击

和回车的效果是一样的

总结

用户主动触发的页面刷新行为(比如刷新按钮、右键刷新、F5 等),会导致浏览器放弃本地缓存,使用协商缓存( 304 缓存)

参考链接

http://www.cnblogs.com/lyzg/p/5125934.html

http://www.alloyteam.com/2016/03/discussion-on-web-caching/

相关链接

浏览器缓存