浏览器的资源请求,如果使用了缓存基本上是两种情况: status code: 200 ok (from cache)和 status code 304 Not Modified。

上面两种方式有什么区别呢?简单地说,第一种方式是不向浏览器发送请求,直接使用本地缓存文件。第二种方式,浏览器虽然发现了本地有该资源的缓存,但是不确定是否是最新的,于是想服务器询问,若服务器认为浏览器的缓存版本还可用,那么便会返回 304。

浏览器关于缓存使用的决策

那么,浏览器如何决定是使用哪种方式呢?这就和服务器在请求返回中的 Header 字段有关了。下面对相关的字段进行简单介绍。

Cache-Control

Cache-Control 是最重要的规则。这个字段用于指定所有缓存机制在整个请求 / 响应链中必须服从的指令。该字段通常覆盖默认缓存算法。另外,缓存指令是单向的,即请求中存在一个指令并不意味着响应中将存在同一个指令。

简单地说,该字段用于控制浏览器在什么情况下直接使用本地缓存而不向服务器发送请求。一般具有以下值:

  • public: 所有内容都将被缓存

  • private: 内容只缓存到似有缓存中

  • no-cache: 所有内容都不会被缓存

  • no-store: 所有内容都不会被缓存到缓存或者 internet 临时文件中

  • must-revalidation/proxy-revalidation: 如果缓存的内容失效,请求必须发送到服务器 / 代理以进行重新验证

max-age=xxx(xxx is numeric): 缓存的内容将在 xxx 秒后失效, 这个选项只在 HTTP 1.1 可用, 并如果和 Last-Modified 一起使用时, 优先级较高

其中最常用的属性便是 max-age, 这个字段很简单,就是浏览器在资源成功请求后的制定时间内,都将直接调用本地缓存和不会向服务器去请求数据。

Expires

Expires 头部字段提供一个日期和时间,在该日期前的所有对该资源的请求都会直接使用浏览器缓存而不用向服务器请求(注意:cache-control max-age 和 s-maxage 将覆盖 Expires 头部。)

Expires 字段接收以下格式的值:“Expires: Sun, 08 Nov 2009 03:37:26 GMT”

但是使用 Expires 存在服务器端时间和浏览器时间不一致的问题

Last-Modified/E-tag

Last-Modified 和 E-tag 的作用都是向服务器确认当前缓存文件是否为最新。抛开功能不看,这两个字段的表现如下:

若服务器在响应一个资源时添加了 Last-Modified 字段,那么当下一次浏览器再一次向服务器请求该资源时(前提是浏览器中上一次的资源被缓存过了),会在请求 header 中包含 If-Modified-Since 字段,且值与服务器第一次响应给浏览器的 Last-Modified 字段一致

若服务器在响应一个资源时添加了 ETag 字段,那么当下一次浏览器再一次向服务器请求该资源时(前提是浏览器中上一次的资源被缓存过了),会在请求 header 中包含 If-None-Match 字段,且值与服务器第一次响应给浏览器的 ETag 字段一致

那么上述是遵循了 Http 协议的浏览器会自动实现的,而要实现 304 的功能,就需要服务器(比如 Apache 对于静态资源会自动实现这两个字段的响应)或者我们手动在服务器端编写响应的逻辑来实现。

若服务器在收到的资源请求中发现含有 Last-Modified 字段,则说明浏览器中包含了该资源的某一版本的缓存,此时服务器端将根据该字段的值进行一定的逻辑判断,以决定让浏览器直接使用已有的缓存(返回 304)还是将最新的文件发送过去(200,发送新文件并更新 Last-Modified 字段)

若服务器在收到的资源请求中发现含有 If-None-Matc 字段,则说明浏览器中包含了该资源的某一版本的缓存,此时服务器端将根据该字段的值进行一定的逻辑判断,以决定让浏览器直接使用已有的缓存(返回 304)还是将最新的文件发送过去(200,发送新文件,并更新 ETag)

若同时使用了 Last-Modified 和 ETag,正确的做法应该是当两者都符合条件时,才返回 304

什么时候使用 ETag?

Etag 主要为了解决 Last-Modified 无法解决的一些问题。

一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新 GET。这种情况下可以将某个能用来表明文件内容是否被更改的值(比如 md5)来作为 ETag

某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说 1s 内修改了 N 次),If-Modified-Since 能检查到的粒度是 s 级的,这种修改无法判断(或者说 UNIX 记录 MTIME 只能精确到秒),

某些服务器不能精确的得到文件的最后修改时间。

不同的页面打开方式产生的请求区别,
一般我们打开(或者更新)一个页面(或者资源)有几种方式:

  • 在地址栏中输入地址,然后回车
  • 激活当前页面地址,然后回车
  • F5 刷新页面
  • 单机 Back/Forward 按钮

上面几种方式对资源的请求,会产生不同的结果,并且各浏览器的表现并不一致。具体的区别可以参考鸟哥的《浏览器缓存机制》


其中大家需要注意的一点是,刷新页面(F5 或者刷新按钮),不管是否设置了 max-age,都会重新像服务器发送请求。但是这不影响 304 逻辑。

本文地址 https://shaoshilei.com/2014-08/re-note-browser-cache-200-from-cache-and-304-summary.html