浏览器缓存机制

本文整理在,我的github上。欢迎Star。

关于3xx系列的状态码

  • 300 多资源选择
  • 301 永久重定向
  • 302 临时重定向
  • 303 告诉用户,使用另一个url获取资源

主要目的是允许POST请求的响应将客户端定位到某个资源上。比如说,在文件上传完成后让客户端自动重定向到一个上传成功的结果页面。

  • 307 通知用户临时从其他URL响应,重定向的页面展示给用户,让用户去点重定向URL链接。
  • 304 服务端没有变更

通知浏览器,可以使用缓存。这篇博文要描述的重点

浏览器(http)缓存机制

类型

说到浏览器缓存,通常会遇到一下两种。

  • 200(from cache)
  • 304

那么,这两种http状态,对应什么样的机制,流程呢?

缓存机制

综合以上描述,浏览器在请求一个资源时,使用缓存的流程大概如下。

  1. 首先浏览器会判断,这个资源是否有缓存。没有的话,正常请求。
  2. 如果有缓存的话,浏览会判断缓存是否过期。如果缓存没有过期,则直接使用。此时就是 200(from cache)

通过上次缓存留下的:cache-control max-age 和 Expires。
注意:cache-control的优先级高于Expires。

  1. 如果浏览器的缓存过期了,它会请求服务器。服务器会校验缓存的数据是否真的发生了更改。如果服务器端发现数据没有变。就会返回一个304告诉浏览器,你请求的数据 “Not Modified”,可以继续用缓存。同时,浏览器会更新缓存首部的过期时间等信息。

这里浏览器发起请求时,会用到上次缓存首部的Last-Modified/E-tag。
具体做法是:
取出上次缓存的Last-Modified的值,放在本次请求header的 If-Modified-Since 中。
取出上次缓存的E-tag的值,放在本次请求header中的 If-None-Match 中。
服务器会据此判断资源是否发生过修改,浏览器中的缓存是否依然可用。

  1. 如果服务器端修改了上次缓存的内容,则直接返回200,并携带新的内容。
![浏览器缓存流程图](https://img2018.cnblogs.com/blog/395192/201904/395192-20190411010046819-1241623313.png)

相关http头

上文中提到了几个http头,这里做一下详细的解释。

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一起使用时, 优先级较高

Expires

头部字段提供一个日期和时间。
(cache-control max-age 和 s-maxage 将覆盖 Expires 头部。)

Last-Modified/E-tag

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

  • 若服务器在响应一个资源时添加了ETag字段,那么当下一次浏览器再一次向服务器请求该资源时(前提是浏览器中上一次的资源被缓存过了),会在请求header中包含If-None-Match字段,且值与服务器第一次响应给浏览器的ETag字段一致
    那么上述是遵循了Http协议的浏览器会自动实现的,而要实现304的功能,就需要服务器(比如Apache对于静态资源会自动实现这两个字段的响应)或者我们手动在服务器端编写响应的逻辑来实现。

写到这,整个浏览器及http缓存机制应该彻底介绍清楚了。
那么,就只剩下如何应用了。

原理与应用

静态资源

所谓静态资源,就是很少会修改的内容,包括前端常年打交道的css、js及图片文件等等。
通常情况下,静态资源的http头是这样配置的。

Cache-Control: public, max-age=31536000
Expires: (一年后的今天)
ETag: (基于内容生成)
Last-Modified: (过去某个时间)
Vary: Accept-Encoding

到这里,可以据此给站点中全部的静态资源配置缓存头。这里我有两个建议。

  • 建议给每个静态资源路径添加指纹信息。可以使用gulp或者webpack等构建工具。通过不同版本的指纹信息控制更新缓存。
  • 项目中引用的依赖,建议使用CDN资源。依据缓存原理,浏览器只要访问过相同CDN的资源,就会留下缓存,哪怕其他站点。

动态资源

基础配置

对于非私密性和经常性变动的资源,我们可以这样做。

Cache-Control: public, max-age=0
Expires: (当前时间)
ETag: (基于内容生成)
Last-Modified: (过去某个时间)
Vary: Accept-Encoding

这样配置的意义在于,内容可以被浏览器缓存起来。但是因为过期时间的问题,每次都会到服务端判断是否过期。未过期的话,就会收到304,进而继续使用缓存。

控制返回、前进

你如果需要更严格的控制,需要告知浏览器即使当用户点击了「返回/前进」按钮,也需要重新检查这些资源文件,那么可以使用:

Cache-Control: public, no-cache, no-store

私密性缓存

对于私密或者针对用户的内容,需要把 public 替换为 private 以避免内容被代理缓存。

Cache-Control: private, …

关于ETag

建议避免使用 ETag。尤其是使用Apache,又配置了负载均衡的情况下。

针对生成的 ETag,默认的Apache方法需要把文件的索引节(inode),大小(size)和最后修改时间作为输入求值得到。这会导致在负载均衡的环境中,生成的 ETag 值变得毫无用处,因为每个服务器都会针对相同的文件生成一个不同的 Etag 值。这个可能就是唯一的问题导致很多人完全禁用 ETag,其实只要精确地针对一个匹配的文件生成一个独一无二的 ETag 值,就没有必要禁用 ETag 了。

Vary: Accept-Encoding

指定“Vary: Accept-Encoding”标头,用一句话来说明它的意义,就是“告诉代理服务器缓存两种版本的资源:压缩和非压缩,这有助于避免一些公共代理不能正确地检测Content-Encoding标头的问题。
还有一个现实的原因:IE 浏览器不缓存任何带有 Vary 头但值不为 Accept-Encoding 和 User-Agent 的资源。所以通过这种方式添加这个头,才能确保这些资源在 IE 下被缓存。
总的来说,加这么一句肯定没错,相当于“多喝热水”。

参考文献

posted @ 2019-04-11 01:06  大黑兔  阅读(812)  评论(0编辑  收藏  举报