浏览器缓存机制

最近看到一篇比较好的关于浏览器缓存的文章,原文链接, 原文内容如下

浏览器缓存机制,其实主要就是 HTTP 协议定义的缓存机制(如: Expires; Cache-control 等)。但是也有非 HTTP 协议定义的缓存机制,如使用 HTML Meta 标签,Web 开发者可以在 HTML 页面的 <head> 节点中加入 < meta > 标签,代码如下:

<META HTTP-EQUIV="Pragma" CONTENT="no-cache">  

上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。使用上很简单,但只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析 HTML 内容本身。

下面我主要介绍 HTTP 协议定义的缓存机制。

Expires 策略

Expires 是 Web 服务器响应消息头字段,在响应 http 请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。

下面是宝宝 PK 项目中,浏览器拉取 jquery.js web 服务器的响应头:

【注:Date 头域表示消息发送的时间,时间的描述格式由 rfc822 定义。例如,Date: Mon,31 Dec 2001 04:25:57GMT。】

Web 服务器告诉浏览器在 2012-11-28 03:30:01 这个时间点之前,可以使用缓存文件。发送请求的时间是 2012-11-28 03:25:01,即缓存 5 分钟。

不过 Expires 是 HTTP 1.0 的东西,现在默认浏览器均默认使用 HTTP 1.1,所以它的作用基本忽略。

Cache-control 策略(重点关注)

Cache-Control 与 Expires 的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过 Cache-Control 的选择更多,设置更细致,如果同时设置的话,其优先级高于 Expires。

http 协议头 Cache-Control :
值可以是 public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age
各个消息中的指令含义如下:
| 值 | 含义 |
|:--|:--:|
|public | 指示响应可被任何缓存区缓存 |
|private | 指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效 |
|no-cache | 指示请求或响应消息不能缓存 |
|no-store | 用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存 |
|max-age | 指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应 |
|min-fresh | 指示客户机可以接收响应时间小于当前时间加上指定时间的响应 |
|max-stale | 指示客户机可以接收超出超时期间的响应消息。如果指定 max-stale 消息的值,那么客户机可以接收超出超时期指定值之内的响应消息 |

还是上面那个请求,web 服务器返回的 Cache-Control 头的值为 max-age=300,即 5 分钟(和上面的 Expires 时间一致,这个不是必须的)。

Last-Modified/If-Modified-Since

Last-Modified/If-Modified-Since 要配合 Cache-Control 使用。

  • Last-Modified:标示这个响应资源的最后修改时间。web 服务器在响应请求时,告诉浏览器资源的最后修改时间
  • If-Modified-Since:当资源过期时(使用 Cache-Control 标识的 max-age),发现资源具有 Last-Modified 声明,则再次向 web 服务器请求时带上头 If-Modified-Since,表示请求时间。web 服务器收到请求后发现有头 If-Modified-Since 则与被请求资源的最后修改时间进行比对。

若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应 HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的 cache。

Etag/If-None-Match

Etag/If-None-Match 也要配合 Cache-Control 使用。

  • Etag:web 服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定)。Apache 中,ETag 的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行 Hash 后得到的。
  • If-None-Match:当资源过期时(使用 Cache-Control 标识的 max-age),发现资源具有 Etage 声明,则再次向 web 服务器请求时带上头 If-None-Match (Etag 的值)。web 服务器收到请求后发现有头 If-None-Match 则与被请求资源的相应校验串进行比对,决定返回 200 或 304。

既生 Last-Modified 何生 Etag?

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

  • Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在 1 秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
  • 如果某些文件会被定期生成,当有时内容并没有任何变化,但 Last-Modified 却改变了,导致文件没法使用缓存
  • 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形

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

用户行为与缓存

浏览器缓存行为还有用户的行为有关!!!

用户操作Expires/Cache-ControlLast-Modified/Etag
地址栏回车有效有效
页面链接跳转有效有效
新开窗口有效有效
前进、后退有效有效
F5 刷新无效有效
Ctrl+F5 刷新无效无效

总结

浏览器第一次请求:

浏览器再次请求时: