0

    浏览器缓存机制剖析

    2023.08.01 | admin | 139次围观

    从⚠️ Provisional headers are shown 和Date字段可以看出来, 浏览器并未发出请求, 缓存依然有效, 只不过此时Status Code显示为200 OK. (甚至我还专门打开了charles, 也没有发现该资源的任何请求, 可见这个200 OK多少有些误导人的意味)

    可见, 启发式缓存算法采用的缓存时间可长可短, 因此对于常规资源, 建议明确设置缓存时间(如指定max-age 或 expires).

    ETag

    ETag:"fcb82312d92970bdf0d18a4eca08ebc7efede4fe"

    实体标签, 服务器资源的唯一标识符, 浏览器可以根据ETag值缓存数据, 节省带宽. 如果资源已经改变, etag可以帮助防止同步更新资源的相互覆盖. ETag 优先级比 Last-Modified 高.

    If-Match

    语法: If-Match: ETag_value 或者 If-Match: ETag_value, ETag_value, …

    缓存校验字段, 其值为上次收到的一个或多个etag 值. 常用于判断条件是否满足, 如下两种场景:

    If-None-Match

    语法: If-None-Match: ETag_value 或者 If-None-Match: ETag_value,ETag_value浏览器缓存机制有几种, …

    缓存校验字段, 结合ETag字段, 常用于判断缓存资源是否有效, 优先级比If-Modified-Since高.

    Last-Modified

    语法: Last-Modified: 星期,日期 月份 年份 时:分:秒 GMT

    Last-Modified: Tue, 04 Apr 2017 10:01:15 GMT

    用于标记请求资源的最后一次修改时间, 格式为GMT(格林尼治标准时间). 如可用 new Date().toGMTString()获取当前GMT时间. Last-Modified 是 ETag 的fallback机制, 优先级比 ETag 低, 且只能精确到秒, 因此不太适合短时间内频繁改动的资源. 不仅如此, 服务器端的静态资源, 通常需要编译打包, 可能出现资源内容没有改变, 而Last-Modified却改变的情况.

    If-Modified-Since

    语法同上, 如:

    If-Modified-Since: Tue, 04 Apr 2017 10:12:27 GMT

    缓存校验字段, 其值为上次响应头的Last-Modified值, 若与请求资源当前的Last-Modified值相同, 那么将返回304状态码的响应, 反之, 将返回200状态码响应.

    If-Unmodified-Since

    缓存校验字段,语法同上. 表示资源未修改则正常执行更新浏览器缓存机制有几种, 否则返回412(Precondition Failed)状态码的响应. 常用于如下两种场景:

    强缓存

    一旦资源命中强缓存, 浏览器便不会向服务器发送请求, 而是直接读取缓存. Chrome下的现象是 200 OK (from disk cache) 或者 200 OK (from memory cache). 如下:

    对于常规请求, 只要存在该资源的缓存, 且Cache-Control:max-age 或者expires没有过期, 那么就能命中强缓存.

    协商缓存

    缓存过期后, 继续请求该资源, 对于现代浏览器, 拥有如下两种做法:

    以上, ETag优先级比Last-Modified高, 同时存在时, 前者覆盖后者. 下面通过实例来理解下强缓存和协商缓存.

    如下忽略首次访问, 第二次通过 If-Modified-Since 命中了304协商缓存.

    协商缓存的响应结果, 不仅验证了资源的有效性, 同时还更新了浏览器缓存. 主要更新内容如下:

    Age:0

    Cache-Control:max-age=600

    Date: Wed,05Apr201713:09:36GMT

    Expires:Wed,05Apr201700:55:35GMT

    Age:0 表示命中了代理服务器的缓存, age值为0表示代理服务器刚刚刷新了一次缓存.

    Cache-Control:max-age=600 覆盖 Expires 字段, 表示从Date_value, 即 Wed, 05 Apr 2017 13:09:36 GMT 起, 10分钟之后缓存过期. 因此10分钟之内访问, 将会命中强缓存, 如下所示:

    当然, 除了上述与缓存直接相关的字段外, http header中还包括如下间接相关的字段.

    Age

    出现此字段, 表示命中代理服务器的缓存. 它指的是代理服务器对于请求资源的已缓存时间, 单位为秒. 如下:

    浏览器缓存机制剖析

    Age:2383321

    Date:Wed,08Mar201716:12:42GMT

    以上指的是, 代理服务器在2017年3月8日16:12:42时向源服务器发起了对该资源的请求, 目前已缓存了该资源2383321秒.

    Date

    指的是响应生成的时间. 请求经过代理服务器时, 返回的Date未必是最新的, 通常这个时候, 代理服务器将增加一个Age字段告知该资源已缓存了多久.

    Vary

    对于服务器而言, 资源文件可能不止一个版本, 比如说压缩和未压缩, 针对不同的客户端, 通常需要返回不同的资源版本. 比如说老式的浏览器可能不支持解压缩, 这个时候, 就需要返回一个未压缩的版本; 对于新的浏览器, 支持压缩, 返回一个压缩的版本, 有利于节省带宽, 提升体验. 那么怎么区分这个版本呢, 这个时候就需要Vary了.

    服务器通过指定Vary: Accept-Encoding, 告知代理服务器, 对于这个资源, 需要缓存两个版本: 压缩和未压缩. 这样老式浏览器和新的浏览器, 通过代理, 就分别拿到了未压缩和压缩版本的资源, 避免了都拿同一个资源的尴尬.

    Vary:Accept-Encoding,User-Agent

    如上设置, 代理服务器将针对是否压缩和浏览器类型两个维度去缓存资源. 如此一来, 同一个url, 就能针对PC和Mobile返回不同的缓存内容.

    怎么让浏览器不缓存静态资源

    实际上, 工作中很多场景都需要避免浏览器缓存, 除了浏览器隐私模式, 请求时想要禁用缓存, 还可以设置请求头: Cache-Control: no-cache, no-store, must-revalidate .

    当然, 还有一种常用做法: 即给请求的资源增加一个版本号, 如下:

    这样做的好处就是你可以自由控制什么时候加载最新的资源.

    不仅如此, HTML也可以禁用缓存, 即在页面的

    节点中加入标签, 代码如下:

    上述虽能禁用缓存, 但只有部分浏览器支持, 而且由于代理不解析HTML文档, 故代理服务器也不支持这种方式.

    IE8的异常表现

    实际上, 上述缓存有关的规律, 并非所有浏览器都完全遵循. 比如说IE8.

    资源缓存是否有效相关.

    浏览器前提操作表现正常表现

    IE8

    资源缓存有效

    新开一个窗口加载网页

    重新发送请求(返回200)

    展示缓存的页面

    IE8

    资源缓存失效

    原浏览器窗口中单击 Enter 按钮

    展示缓存的页面

    重新发送请求(返回200)

    Last-Modified / E-Tag 相关.

    浏览器前提操作表现正常表现

    IE8

    资源内容没有修改

    新开一个窗口加载网页

    浏览器重新发送请求(返回200)

    重新发送请求(返回304)

    IE8

    资源内容已修改

    原浏览器窗口中单击 Enter 按钮

    浏览器展示缓存的页面

    重新发送请求(返回200)

    参考文章

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论