整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

一文搞懂浏览器缓存机制

近在项目中遇到了 IE浏览器因缓存问题未能成功向后端发送 GET类型请求 的bug,然后顺藤摸瓜顺便看了看缓存的知识,觉得有必要总结跟大家分享一下。

在前端开发中,性能一直都是被大家所重视的一点,然而判断一个网站的性能最直观的就是看网页打开的速度。其中提高网页反应速度的一个方式就是使用缓存。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。

1. 介绍

web缓存是指一个web资源(如html页面,图片,js,数据等)存在于web服务器和客户端(浏览器)之间的副本。

缓存会根据进来的请求保存输出内容的副本;当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。至于浏览器和网站服务器是如何标识网站页面是否更新的机制,将在后面介绍。

1.1 web缓存的作用

web缓存的作用显而易见:

  1. 减少网络带宽消耗:无论对于网站运营者或者用户,带宽都代表着金钱,过多的带宽消耗,只会便宜了网络运营商。当Web缓存副本被使用时,只会产生极小的网络流量,可以有效的降低运营成本。
  2. 降低服务器压力:给网络资源设定有效期之后,用户可以重复使用本地的缓存,减少对源服务器的请求,间接降低服务器的压力。同时,搜索引擎的爬虫机器人也能根据过期机制降低爬取的频率,也能有效降低服务器的压力。
  3. 减少网络延迟,加快页面打开速度:带宽对于个人网站运营者来说是十分重要,而对于大型的互联网公司来说,可能有时因为钱多而真的不在乎。那Web缓存还有作用吗?答案是肯定的,对于最终用户,缓存的使用能够明显加快页面打开速度,达到更好的体验。

1.2 web缓存的类型

web缓存大致可以分为以下几种类型 详细内容:

  1. 数据库数据缓存
  2. 服务器端缓存
  3. 浏览器端缓存
  4. web应用层缓存

浏览器通过代理服务器向源服务器发起请求的原理如下图:

浏览器先向代理服务器发起web请求,再将请求转发到源服务器。它属于共享缓存,所以很多地方都可以使用其缓存资源,因此对于节省流量有很大作用。

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

下面着重关注一下浏览器缓存。

2. web缓存的工作原理

所有的缓存都是基于一套规则来帮助他们决定什么时候使用缓存中的副本提供服务(假设有副本可用的情况下,未被销毁回收或者未被删除修改)。这些规则有的在协议中有定义(如HTTP协议1.0和1.1),有的则是由缓存的管理员设置(如DBA、浏览器的用户、代理服务器管理员或者应用开发者)。

2.1 浏览器端的缓存规则

对于浏览器端的缓存来讲,这些规则是在HTTP协议头和HTML页面的 Meta标签中定义的。他们分别从新鲜度校验值两个维度来规定浏览器是直接使用缓存中的副本,还是需要去源服务器获取更新的版本。

  1. 新鲜度(过期机制):也就是缓存副本有效期。一个缓存副本必须满足以下任一条件,浏览器会认为它是有效的,足够新的,而直接从缓存中获取副本并渲染:
  • 含有完整的过期时间控制头信息(HTTP协议报头),并且仍在有效期内
  • 浏览器已经使用过这个缓存副本,并且在一个会话中已经检查过新鲜度
  1. 校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签Etag(Entity Tag),它可以用来作为浏览器再次请求过程的校验标识。如过发现校验标识不匹配,说明资源已经被修改或过期,浏览器需求重新获取资源内容。

2.2 浏览器缓存的控制

2.2.1 使用HTML的 Meta 标签

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

上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。使用上很简单,但只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。可以通过这个页面测试你的浏览器是否支持:[Pragma No-Cache Test] (http://www.procata.com/cachetest/tests/pragma/index.php)。

2.2.2 使用缓存有关的HTTP消息报头

一个URI的完整HTTP协议交互过程是由HTTP请求和HTTP响应组成的。有关HTTP详细内容可参考《Hypertext Transfer Protocol — HTTP/1.1》、《HTTP协议详解》等。

在HTTP请求和响应的消息报头中,常见的与缓存有关的消息报头有:

稍微解释一下:

1. Cache-Control

  1. max-age(单位为s)指定设置缓存最大的有效时间,定义的是时间长短。当浏览器向服务器发送请求后,在max-age这段时间里浏览器就不会再向服务器发送请求了。我们来找个资源看下。比如QQ推广上的css资源,max-age=3600,也就是说缓存有效期为3600秒(也就是1h)。于是在1天内都会使用这个版本的资源,即使服务器上的资源发生了变化,浏览器也不会得到通知。max-age会覆盖掉Expires,后面会有讨论


  1. s-maxage(单位为s)同max-age,只用于共享缓存(比如CDN缓存)。比如,当s-maxage=60时,在这60秒中,即使更新了CDN的内容,浏览器也不会进行请求。也就是说max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage,则会覆盖掉max-age和Expires header。
  2. public 指定响应会被缓存,并且在多用户间共享。也就是下图的意思。如果没有指定public还是private,则默认为public。


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


  1. no-cache 指定不缓存响应,表明资源不进行缓存,但是设置了 no-cache 之后并不代表浏览器不缓存,而是在获取缓存前要向服务器确认资源是否被更改。因此有的时候只设置 no-cache 防止缓存还是不够保险,还可以加上 private 指令,将过期时间设为过去的时间。
  2. no-store 绝对禁止缓存,一看就知道如果用了这个命令当然就是不会进行缓存啦~每次请求资源都要从服务器重新获取。
  3. must-revalidate 指定如果页面是过期的,则去服务器进行获取。这个指令并不常用,就不做过多的讨论了。

cache-control的种类这么多,然而怎么使用它们呢,参看下图:

2. Expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires=max-age + 请求时间 ,需要和Last-modified结合使用。但在上面我们提到过,cache-control的优先级更高。Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。

3. Last-modified & If-modified-since

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

4. Etag & & If-None-Match

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

2.2.3 缓存报头种类与优先级

1. Cache-Control与Expires

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

2. Last-Modified与ETag

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

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

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。Etag的服务器生成规则和强弱Etag的相关内容可以参考,《互动百科-Etag》和《HTTP Header definition》,这里不再深入。

3. Last-Modified/ETag 与 Cache-Control/Expires

配置 Last-Modified/ETag的情况下,浏览器再次访问统一URI的资源,还是会发送请求到服务器询问文件是否已经修改,如果没有,服务器会只发送一个304回给浏览器,告诉浏览器直接从自己本地的缓存取数据;如果修改过那就整个数据重新发给浏览器;

Cache-Control/Expires则不同,如果检测到本地的缓存还是有效的时间范围内,浏览器直接使用本地副本,不会发送任何请求。两者一起使用时, Cache-Control/Expires的优先级要高,即当本地副本根据 Cache-Control/Expires发现还在有效期内时,则不会再次发送请求去服务器询问修改时间 Last-Modified或实体标识 Etag了。

一般情况下,两者会配合一起使用,因为即使服务器设置缓存时间, 当用户点击“刷新”按钮时,浏览器会忽略缓存继续向服务器发送请求,这时 Last-Modified/ETag将能够很好利用304,从而减少响应开销。

2.2.4 哪些请求不能被缓存?

无法被浏览器缓存的请求:

  • HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0等告诉浏览器不用缓存的请求
  • 需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
  • 经过HTTPS安全加密的请求(有人也经过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public之后,能够对HTTPS的资源进行缓存,参考《HTTPS的七个误解》)
  • POST请求无法被缓存
  • HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存

3. 使用缓存流程

一个用户发起一个静态资源请求的时候,浏览器会通过以下几步来获取并展示资源:

缓存行为主要由缓存策略决定,而缓存策略由内容拥有者设置。这些策略主要通过特定的HTTP头部来清晰地表达。

以上过程也可以被概括为三个阶段:

  1. 本地缓存阶段:先在本地查找该资源,如果有发现该资源,而且该资源还没有过期,就使用这一个资源,完全不会发送http请求到服务器;
  2. 协商缓存阶段:如果在本地缓存找到对应的资源,但是不知道该资源是否过期或者已经过期,则发一个http请求到服务器,然后服务器判断这个请求,如果请求的资源在服务器上没有改动过,则返回304,让浏览器使用本地找到的那个资源;
  3. 缓存失败阶段:当服务器发现请求的资源已经修改过,或者这是一个新的请求(在本来没有找到资源),服务器则返回该资源的数据,并且返回200, 当然这个是指找到资源的情况下,如果服务器上没有这个资源,则返回404。

4. 用户操作行为与缓存的关系

用户在使用浏览器的时候,会有各种操作,比如输入地址后回车,按F5刷新等,这些行为会对缓存有什么影响呢?

通过上表我们可以看到,当用户在按 F5进行刷新的时候,会忽略Expires/Cache-Control的设置,会再次发送请求去服务器请求,而Last-Modified/Etag还是有效的,服务器会根据情况判断返回304还是200;

而当用户使用 Ctrl+F5进行强制刷新的时候,只是所有的缓存机制都将失效,重新从服务器拉去资源。

  • 普通刷新 – 当按下F5或者点击刷新按钮来刷新页面的时候,浏览器将绕过本地缓存来发送请求到服务器, 此时, 协商缓存是有效的
  • 强制刷新 – 当按下ctrl+F5来刷新页面的时候, 浏览器将绕过各种缓存(本地缓存和协商缓存), 直接让服务器返回最新的资源
  • 回车或转向 – 当在地址栏上输入回车或者按下跳转按钮的时候, 所有缓存都生效

5. 如何从缓存角度改善站点

  • 同一个资源保证URL的稳定性
  • 给css、js、图片等资源增加HTTP缓存头,并强制入口html不被缓存
  • 减少对Cookie的依赖
  • 多用Get方式请求动态Cgi
  • 动态CGI也是可以被缓存

关注微信公众号:安徽思恒信息科技有限公司,了解更多技术内容……

近一次移动端Vue应用的上线,导致某些用户使用某些功能时出现问题,经主动清空缓存后恢复。有时候清空微信应用的存储空间缓存仍不能解决问题,此时安卓机可借助微信TBS调试工具 http://debugx5.qq.com (微信中打开页面,勾选最下面四个选项清除缓存),但该工具目前只支持安卓手机,苹果机就比较麻烦了。为了找到问题的本质,从根本上避免问题,最近浏览了一些文章,其中有一篇对浏览器缓存的分析及在Nginx中对应的处理策略总结的比较好,这里分享给大家。

  • 原文标题:http常用缓存策略及vue-cli单页面应用服务器端(nginx)如何设置缓存
  • 原文地址:http://www.shanhuxueyuan.com/news/detail/125.html

以下为原文。

关于http或者是浏览器缓存策略,我认为可以分为这三种:

  • 不使用缓存
  • 强制使用缓存
  • 协商使用缓存

不使用缓存

有时,我们希望浏览器永远都不要使用缓存,全部到服务器拉取数据,此时即为不使用缓存,我们可以在服务端通过Cache-Control为 no-store实现。

服务器端针对上面文件设置了no-store,可以看到在请求的时候,无论怎么刷新,都是返回200,不会显示304,也不会显示“memory cache”或“disk cache”,说明真的都是从服务器重新拉取数据。

比如我们想设置html文件不缓存,可以在域名的解析配置中如下设置,当文件后缀为html或htm时add_header Cache-Control "no-store"

server {
listen 80;
server_name yourdomain.com;
location / {
    try_files $uri $uri/ /index.html;
    root /yourdir/;
    index index.html index.htm;

    if ($request_filename ~* .*\.(?:htm|html)$)
    {
        add_header Cache-Control "no-store";  //对html文件设置永远不缓存
    }  
  }
}

这种方式缺点就是每次都要去服务端拉取文件,即使文件没有更新,很明显这样增加了不必要的带宽消耗。

如果文件没有更新,我们就使用缓存,只有更新了才去拉取最新文件,这样多好,这就是协商缓存。

协商缓存

协商缓存就是浏览器携带文件缓存标识(如Last-Modified或ETag),向服务器发送请求,由服务器根据文件缓存标识来决定是否使用缓存,如果文件没有更新,则告诉浏览器使用本地缓存,如果文件更新了,则直接返回新文件内容。

可以看出,相比不使用缓存,协商缓存是会大大减少带宽消耗的。

  • 协商缓存生效,返回304 和 Not Modified

  • 协商缓存无效,返回200和请求文件

我们在浏览器调试页面,可以看到有304的,即是使用了协商缓存

服务器返回的header中会有Last-Modified和ETag标识,而浏览器请求header中会包含If-Modified-Since和If-None-Match

Last-Modified和If-Modified-Since

在 http 1.0 版本中,第一次请求资源时服务器通过 Last-Modified 来设置响应头的缓存标识,并且把资源最后修改的时间作为值填入,然后将资源返回给浏览器。在第二次请求时,浏览器会首先带上 If-Modified-Since 请求头去访问服务器,服务器会将 If-Modified-Since 中携带的时间与资源修改的时间匹配,如果时间不一致,服务器会返回新的资源,并且将 Last-Modified 值更新,作为响应头返回给浏览器。如果时间一致,表示资源没有更新,服务器返回 304 状态码,浏览器拿到响应状态码后从本地缓存数据库中读取缓存资源。

这种方式有2个弊端,第一个就是当服务器中的资源增加了一个字符,后来又把这个字符删掉,本身资源文件并没有发生变化,但修改时间发生了变化。当下次请求过来时,服务器也会把这个本来没有变化的资源重新返回给浏览器;第二个就是修改时间的单位为秒,所以存在1s的间隙,即使更新了,也会认为没有更新。

ETag和If-None-Match

在 http 1.1 版本中,服务器通过 Etag 来设置响应头缓存标识。Etag 的值由服务端生成,可以认为是文件内容的hash值。在第一次请求时,服务器会将资源和 Etag 一并返回给浏览器,浏览器将两者缓存到本地缓存数据库。在第二次请求时,浏览器会将 Etag 信息放到 If-None-Match 请求头去访问服务器,服务器收到请求后,会将服务器中的文件标识与浏览器发来的标识进行对比,如果不相同,服务器返回更新的资源和新的 Etag ,如果相同,服务器返回 304 状态码,浏览器读取缓存。

两者对比

  • 首先在精确度上,Etag要优于Last-Modified。Last-Modified的时间单位是秒,如果某个文件在1秒内改变了多次,那么他们的Last-Modified其实并没有体现出来修改,但是Etag每次都会改变确保了精度;如果是负载均衡的服务器,各个服务器生成的Last-Modified也有可能不一致。
  • 第二在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。
  • 第三在优先级上,服务器校验优先考虑Etag

协商缓存服务端配置

可以在服务端通过设置Cache-Control为 no-cache或者max-age=0来实现

强制缓存

有时我们希望文件强制使用缓存,比如通过vue-cli产生的js和css,文件名上带有hash值,所以如果文件名没有变的时候,我们希望文件永久缓存,这样可以减少网络请求。

强制缓存整体流程比较简单,就是在第一次访问服务器取到数据之后,在过期时间之内不会再去重复请求。实现这个流程的核心就是如何知道当前时间是否超过了过期时间。

强制缓存的过期时间通过第一次访问服务器时返回的响应头获取。在 http 1.0 和 http 1.1 版本中通过不同的响应头字段实现。

在 http 1.0 版本中,强制缓存通过 Expires 响应头来实现。 expires 表示未来资源会过期的时间。也就是说,当发起请求的时间超过了 expires 设定的时间,即表示资源缓存时间到期,会发送请求到服务器重新获取资源。而如果发起请求的时间在 expires 限定的时间之内,浏览器会直接读取本地缓存数据库中的信息(from memory or from disk),两种方式根据浏览器的策略随机获取。

在 http 1.1 版本中,可以设置Cache-Control中的 max-age=xxx ,来表示缓存的资源将在 xxx 秒后过期。一般来说,为了兼容,两个版本的强制缓存都会被实现。

为什么有了Expires,后来又增加了max-age呢,这是因为Expires是一个绝对时间,有可能客户端的时间和服务器不一致,导致缓存不能按照预期进行,而max-age则是个相对时间,比如3600s,自浏览器请求后3600s之内,都使用本地缓存,和客户端的时间没关系。

vue-cli缓存策略

由于打包后的js、css和图片,一般名称都带有hash值,名称中的hash变了,自然会拉取新文件,所以我们可以将这类文件设置为强制缓存,只要文件名不变,就一直缓存,比如缓存100天或者一年。

而html文件则不能设为强制缓存,一般html名称是没法带hash值的,所以html如果设置了强制缓存,则永远也没法更新,html不更新,其引用的js、css等名称也不会更新,则整个服务都没有更新,只能让用户清除缓存了。所以针对html文件,我们可以设置协商缓存或者直接不使用缓存,本身html文件都比较小,我是直接使用了不缓存,nginx配置如下。

server {
listen 80;
server_name yourdomain.com;
location / {
    try_files $uri $uri/ /index.html;
    root /yourdir/;
    index index.html index.htm;

    if ($request_filename ~* .*\.(js|css|woff|png|jpg|jpeg)$)
    {
        expires    100d;  //js、css、图片缓存100天
        #add_header Cache-Control "max-age = 8640000"; //或者设置max-age
    }

    if ($request_filename ~* .*\.(?:htm|html)$)
    {
        add_header Cache-Control "no-store";  //html不缓存
    }
  }
}

欢迎微信搜索关注公众号:半路雨歌,查看更多技术干货文章

缓存,这个问题在面试中出现的频率还是很高的,也是我们日常工作中必知必会的。

所以在本文中,我们来尝试对HTTP 协议中规定的很多请求头响应头来做总结和归纳,让大家了解到浏览器是如何通过他们来控制缓存的

经典 GET 请求过程

下图是一个经典的 GET 请求的处理过程:

当一个请求达到时,浏览器会先检查被访问的资源是否已被缓存。

如果未被缓存(缓存未命中 cache miss),则将请求转发给原始服务器

如果已被缓存(缓存命中,cache hit),则会检查缓存是否足够新鲜

如果缓存的副本足够新鲜,则直接将副本返回客户端,否则会向服务端发起新鲜度验证(revalidation)。如果发现与服务端文件一致,则将本地缓存副本返回给客户端,否则将请求转发给原始服务器。

在这个过程中,由缓存提供的服务在所有请求占比中的比例称为缓存命中率(cache hit rat)。

这种描述方式只能描述请求级别的命中情况,无法体现具体有多少流量来自缓存。

比如一个访问频次很低尺寸很大的文件,如果以该命中率来描述的话,命中率非常低。但是这个文件却占据了绝大多数的访问流量。

因此还需要另一个命中率指标来描述,那就是字节命中率(byte hit rate)。字节命中率表示的是缓存提供的字节在所有传输字节中的占比。

缓存机制

浏览器常用校验缓存的机制有两种:

  • 文档过期(document expiration)
  • 服务端再验证(server revalidation)

下面我们来分别介绍下这两种机制。

文档过期

HTTP 通过 Cache-Control:max-ageExpires 这两个响应头信息, 让原始服务器向每个文档附加一个“过期日期”。在缓存文档过期之前,缓存可以以任意频次使用这些副本,而无需与服务端联系。
Expires 首部与 Cache-Control:max-age 首部本质上是一样的,区别是 Expires 是
HTTP/1.0 协议规定的首部,且首部取值为一个绝对时间,在这个时间之后缓存失效;Cache-Control:max-age 是 HTTP/1.1 协议规定的首部,且首部取值是一个相对时间,单位为

一个绝对,一个相对。

服务端再验证

HTTP 定义了 5 个条件请求首部来完成服务器再验证:

  1. If-Modified-Since
  2. If-None-Match
  3. If-Unmodified-Since
  4. If-Range
  5. If-Match

其中最有用的是 If-Modified-Since If-None-Match 两个首部。

一、If-Modified-Since: Date 再验证

If-Modified-Since: Date 再验证请求流程分两种:

1、自指定日期后,文档被修改了,If-Modified-Since 条件为,GET 请求就会执行。携带新首部的新文档会被返回给缓存,新首部除了其他信息以外,还包含了一个新的过期日期。

2、自指定日期后,文档没有被修改过,If-Modified-Since 条件为,则会向客户端返回一个304 Not Modified 响应报文,为了提高有效性,一般会发送一个新的过期日期,不会返回文档的主体。

If-Modified-Since 请求首部通常与 Last-Modified 服务器响应首部配合工作。原始服务器会将最后的修改日期附加到文档上去。当浏览器要对已缓存的文档进行再验证时,就会包含一个 If-Modified-Since首部,其中携带有最后修改已缓存副本的日期:

If-Modified-Since: <cached last-modified date>

这里以W3C网站为例:




从上面两张图上,可以分别看到请求头信息中的 If-Modified-Since 和响应头信息中的 Last-Modified。

说白了,就是本地记录的修改日期,与服务器那边记录的修改日期做对比。如果服务的比较新,就重新过去,否则加载本地的。

上图我第一次进来,因为本地并没有If-Modified-Since 记录,所以只能看到响应头中有最后修改时间信息,以及返回的200状态码。

当我再次刷新网页之后:



瞧,请求头也有信息了。因为If-Modified-Since 和 Last-Modified两个值是一样的,所以服务器校验本页面为缓存,返回304状态码。


If-None-Match: etag 实体标签验证

在某些情况下,If-Modified-Since: Date 可能无法很好地解决缓存问题。

比如一个被周期性复写的文件,虽然修改日期每次都有变化,但是文件的内容往往是一样的,如果此时还根据最后修改时间去判定是否为缓存,显示是不合适的。

这种情况下,就需要借助实体标签(Etag)验证了。

实体标签就是“版本标识符”,是附加到文档上的任意标签(引用字符串),可能包含了文档的序列号或版本名,或者是文档内容的校验信息。
If-None-Match: etag 实体标签验证的工作过程与 If-Modified-Since: Date 再验证的工作过程基本一致,不同的是,服务器会在响应中附加一个 Etag 响应头。当缓存要对已缓存的文档进行再验证时,就会将这个 etag 放到 If-None-Match 请求头中去。

还是以上面的请求为例:



可以看到,If-none-match 在chrome网络面板中的位置,就是在 If-modifed-since 后面,只是它对应的不是修改日期,而是上面相应的etag罢了。

说简单点,就是hash字符串校验。

注意:如果你是第一次访问该页面,请求头中同样没有 if-none-match 信息。

其他相关属性

一、服务器端响应

服务端通过Cache-Control 来对响应缓存做限制,以下控制优先级按顺序依次递减:

Cache-Control: no-store 禁止缓存对响应进行复制。
Cache-Control: no-cache/ Pragma: no-cache 缓存可以复制响应,但是在与原始服务器进行新鲜度再验证之前不能将其提供给客户端。Pramga: no-cache 为了兼容 HTTP/1.0,优先级低于 Cache-Control: no-cache。
Cache-Control: must-revalidate 在事前没有跟原始服务器进行再验证的情况下,缓存不能提供缓存副本。
Cache-Control: max-age max-age 指定的秒数内有效。max-age 为零时,不可缓存。
Expires: Date 在实际的绝对日期之前有效。

二、客户端请求
客户端同样可以通过 Cache-Control 请求首部来强化或放松对过期时间的限制。以下是使用时的具体参数及说明:

Cache-Control: max-stale=< s > 缓存可以随意提供副本,如果指定的秒数,那么在这段时间内,文档不能过期。
Cache-Control: min-fresh=< s > 至少在未来< s >秒内文档保持新鲜。
Cache-Control: max-age=< s > 缓存无法返回缓存时间超过< s >的文档。如果与 max-stale 通用,max-stale 优先级更高。
Cache-Control: no-cache/Pragma: no-cache 除非进行了再验证,否则客户端不接受已缓存的资源。
Cache-Control: no-store 缓存应该删除本地缓存副本,使用原始服务器响应。
Cache-Control: only-if-cache 只有当缓存中有副本存在时,客户端才会获取一份副本。

总结

好了,缓存就暂时先说到这里。最后让我们再来看下浏览器的缓存执行流程,巩固一下吧。

浏览器首次请求:


浏览器再次请求时: