简单来说缓存会先走强缓存,然后再走协商缓存,默认不缓存。
强缓存受
Expires
或者cache-control
控制。协商缓存受
Last-Modified与If-Modified-Since
和ETag与If-None-Match
控制。
这里有一个特殊情况值得注意:
根据 RFC7234 标准,如果没有设置强缓存的请求头但是有Last-Modified
,会采用启发式算法
1 | 缓存时间 = (Date - Last-Modified) / 10 |
这里有点坑,导致协商缓存不生效
另外还需要注意的是:
用户的一些具体操作会绕过缓存,强制向服务器请求,如下:
另外在日常调试遇到个问题:
上述操作如果是页面嵌套iframe,iframe里面的页面都是有效,不会随着操作清理缓存,操作清理的只是top的缓存。
上述操作在Chrome的PC端尝试地址栏直接回车强制缓存也无效,但是修改地址回车可以命中强制缓存。
设置了强制缓存
cache-control: 3600
,IOS手机端Chrome浏览器不管地址栏回车,页面链接跳转,新开窗口都没有走强制缓存而是向服务器请求(why?)响应头如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17HTTP/1.1 200
Server: nginx
Date: Wed, 07 Jun 2023 09:13:41 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
ETag: W/"1693-1681473628000"
Last-Modified: Fri, 14 Apr 2023 12:00:28 GMT
X-XSS-Protection: 0
Strict-Transport-Security: max-age=31536000
access-control-allow-credentials: true
access-control-allow-headers: *
access-control-allow-origin: *
access-control-expose-headers: *
Cache-Control: max-age=31536000
Content-Encoding: gzip
下面是参考文章归档 ==========
一文搞懂浏览器缓存机制
最近在项目中遇到了 IE浏览器因缓存问题未能成功向后端发送 GET
类型请求 的bug,然后顺藤摸瓜顺便看了看缓存的知识,觉得有必要总结跟大家分享一下。
在前端开发中,性能一直都是被大家所重视的一点,然而判断一个网站的性能最直观的就是看网页打开的速度。其中提高网页反应速度的一个方式就是使用缓存。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。
1. 介绍
web缓存是指一个web资源(如html页面,图片,js,数据等)存在于web服务器和客户端(浏览器)之间的副本。
缓存会根据进来的请求保存输出内容的副本;当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。至于浏览器和网站服务器是如何标识网站页面是否更新的机制,将在后面介绍。
1.1 web缓存的作用
web缓存的作用显而易见:
- 减少网络带宽消耗:无论对于网站运营者或者用户,带宽都代表着金钱,过多的带宽消耗,只会便宜了网络运营商。当Web缓存副本被使用时,只会产生极小的网络流量,可以有效的降低运营成本。
- 降低服务器压力:给网络资源设定有效期之后,用户可以重复使用本地的缓存,减少对源服务器的请求,间接降低服务器的压力。同时,搜索引擎的爬虫机器人也能根据过期机制降低爬取的频率,也能有效降低服务器的压力。
- 减少网络延迟,加快页面打开速度:带宽对于个人网站运营者来说是十分重要,而对于大型的互联网公司来说,可能有时因为钱多而真的不在乎。那Web缓存还有作用吗?答案是肯定的,对于最终用户,缓存的使用能够明显加快页面打开速度,达到更好的体验。
1.2 web缓存的类型
web缓存大致可以分为以下几种类型 详细内容:
- 数据库数据缓存
- 服务器端缓存
- 浏览器端缓存
- web应用层缓存
浏览器通过代理服务器向源服务器发起请求的原理如下图:浏览器先向代理服务器发起web请求,再将请求转发到源服务器。它属于共享缓存,所以很多地方都可以使用其缓存资源,因此对于节省流量有很大作用。
浏览器缓存是将文件保存在客户端,在同一个会话过程中会检查缓存的副本是否足够新,在后退网页时,访问过的资源可以从浏览器缓存中拿出使用。通过减少服务器处理请求的数量,用户将获得更快的体验
下面着重关注一下浏览器缓存。
2. web缓存的工作原理
所有的缓存都是基于一套规则来帮助他们决定什么时候使用缓存中的副本提供服务(假设有副本可用的情况下,未被销毁回收或者未被删除修改)。这些规则有的在协议中有定义(如HTTP协议1.0和1.1),有的则是由缓存的管理员设置(如DBA、浏览器的用户、代理服务器管理员或者应用开发者)。
2.1 浏览器端的缓存规则
对于浏览器端的缓存来讲,这些规则是在HTTP协议头和HTML页面的 Meta
标签中定义的。他们分别从新鲜度和校验值两个维度来规定浏览器是直接使用缓存中的副本,还是需要去源服务器获取更新的版本。
新鲜度(过期机制):也就是缓存副本有效期。一个缓存副本必须满足以下任一条件,浏览器会认为它是有效的,足够新的,而直接从缓存中获取副本并渲染:
- 含有完整的过期时间控制头信息(HTTP协议报头),并且仍在有效期内
- 浏览器已经使用过这个缓存副本,并且在一个会话中已经检查过新鲜度
校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签Etag(Entity Tag),它可以用来作为浏览器再次请求过程的校验标识。如过发现校验标识不匹配,说明资源已经被修改或过期,浏览器需求重新获取资源内容。
2.2 浏览器缓存的控制
######## 2.2.1 使用HTML的 Meta
标签
1 | <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
max-age(单位为s)指定设置缓存最大的有效时间,定义的是时间长短。当浏览器向服务器发送请求后,在max-age这段时间里浏览器就不会再向服务器发送请求了。我们来找个资源看下。比如QQ推广上的css资源,max-age=3600,也就是说缓存有效期为3600秒(也就是1h)。于是在1天内都会使用这个版本的资源,即使服务器上的资源发生了变化,浏览器也不会得到通知。max-age会覆盖掉Expires,后面会有讨论
s-maxage(单位为s)同max-age,只用于共享缓存(比如CDN缓存)。比如,当s-maxage=60时,在这60秒中,即使更新了CDN的内容,浏览器也不会进行请求。也就是说max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage,则会覆盖掉max-age和Expires header。
public 指定响应会被缓存,并且在多用户间共享。也就是下图的意思。如果没有指定public还是private,则默认为public。
private 响应只作为私有的缓存(见下图),不能在用户间共享。如果要求HTTP认证,响应会自动设置为 private
no-cache 指定不缓存响应,表明资源不进行缓存,但是设置了 no-cache 之后并不代表浏览器不缓存,而是在获取缓存前要向服务器确认资源是否被更改。因此有的时候只设置 no-cache 防止缓存还是不够保险,还可以加上 private 指令,将过期时间设为过去的时间。
no-store 绝对禁止缓存,一看就知道如果用了这个命令当然就是不会进行缓存啦~每次请求资源都要从服务器重新获取。
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头部来清晰地表达。
以上过程也可以被概括为三个阶段:
- 本地缓存阶段:先在本地查找该资源,如果有发现该资源,而且该资源还没有过期,就使用这一个资源,完全不会发送http请求到服务器;
- 协商缓存阶段:如果在本地缓存找到对应的资源,但是不知道该资源是否过期或者已经过期,则发一个http请求到服务器,然后服务器判断这个请求,如果请求的资源在服务器上没有改动过,则返回304,让浏览器使用本地找到的那个资源;
- 缓存失败阶段:当服务器发现请求的资源已经修改过,或者这是一个新的请求(在本来没有找到资源),服务器则返回该资源的数据,并且返回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也是可以被缓存
— 网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~
参考:
Web缓存机制系列:http://www.alloyteam.com/2012/03/web-cache-1-web-cache-overview/
浅谈web缓存:http://www.alloyteam.com/2016/03/discussion-on-web-caching/
Web前后端缓存技术:http://blog.csdn.net/LeeSirbupt/article/details/54409931
浏览器强缓存和协商缓存浅解
1. 强缓存
给浏览器缓存设置过期时间,超过这个时间之后缓存过期,浏览器需要重新请求。
expires(老版本http1.0)
给浏览器设置了一个绝对时间用GMT格式的字符串,当浏览器时间超过这个绝对时间之后,重新向服务器发送请求。如下:
1 | Expires: Wed Feb 20 2019 11:25:41 GMT |
弊端: Expires返回的是服务器的时间,但判断的时候用的却是客户端的时间,这就导致Expires很被动,因为用户有可能改变客户端的时间,导致缓存时间判断出错,这也是引入Cache-Control:max-age指令的原因之一。
cache-control(http1.1)
为了解决expires存在的问题,Http1.1版本中提出了cache-control:max-age,该字段与expires的缓存思路相同,都是设置了一个过期时间,不同的是max-age设置的是相对缓存时间开始往后的多少秒
,因此不再受日期不准确情况的影响。
常见的值是max-age
public private no-cache
no-store
- cache-control: max-age=xxxx,public。
客户端和代理服务器都可以缓存该资源;客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,如果用户做了刷新操作,就向服务器发起http请求 - cache-control: max-age=xxxx,private
只让客户端可以缓存该资源;代理服务器不缓存
客户端在xxx秒内直接读取缓存 - cache-control: max-age=xxxx,immutable。
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,即使用户做了刷新操作,也不向服务器发起http请求 - cache-control: no-cache
跳过设置强缓存,但是不妨碍设置协商缓存;一般如果做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端。 - cache-control: no-store
不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了。
强缓存弊端: 强缓存优先级高,如果在过期时间内缓存的资源在服务器上更新了,客服端不能及时获取最新的资源。这时怎么办?于是就有了协商缓存.
2. 协商缓存
协商缓存解决了无法及时获取更新资源的问题。它利用【Last-Modified,If-Modified-Since】
、【ETag、If-None-Match】
两组字段,对资源做标识.然后由服务器做分析,如果资源未更新,则返回304状态码.那么浏览器则会从缓存中读取资源,否则重新请求资源。
1). Last-Modified与If-Modified-Since
浏览器第一次向服务器请求资源
,服务器会在返回这个资源的同时,在response的header加上Last-Modified
的header,这个header表示这个资源在服务器上的最后修改时间:Last-Modified: Wed Feb 20 2021 14:08:32 GMT浏览器之后(第2次+)再向服务器请求这个资源时
,在request的header上加上If-Modified-Since
的header,这个header的值就是上一次请求时返回的Last-Modified的值服务器再次(第2次+)收到资源请求时
,根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified,但是不会返回资源内容
;如果有变化,返回200,就正常返回资源内容。- 浏览器收到304的响应后,就会从缓存中加载资源。(当服务器返回304 Not Modified的响应时,response的header中不会再添加Last-Modified的header,因为既然资源没有变化,那么Last-Modified也就不会改变,这是服务器返回304时的response header.)
- 浏览器收到200的响应后,则从服务器加载新资源时,Last-Modified Header在重新加载的时候会被更新,下次请求时,If-Modified-Since会启用上次返回的Last-Modified值。
弊端:
由于是根据服务器最后修改时间
返回的header
- 它们是以秒为单位进行更新,如果小于该单位高频进行更新的话,则不适合采用该方法。
- 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新get;
- 某些服务器不能精确的得到文件的最后修改时间。
2). ETag与If-None-Match
浏览器第一次跟服务器请求一个资源
,服务器在返回这个资源的同时,在response的header加上ETag
的header,这个header是服务器根据当前请求的资源生成的一个唯一标识字符串,如ETag:baitedangsi,只要资源有变化这个串就不同,跟最后修改时间没有关系
,所以能很好的补充Last-Modified的问题.浏览器再次(第二次+)跟服务器请求这个资源时
,在request的header上加上If-None-Match
的header,这个header的值就是上一次请求时返回的ETag的值。服务器再次(第二次+)收到资源请求时
,根据浏览器传过来If-None-Match
和服务器根据资源生成一个新的ETag
对比,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容
;如果有变化,则返回200,并正常返回资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化- 浏览器收到304的响应后,就会从缓存中加载资源。
- 浏览器收到200的响应后,则从服务器加载新资源时,
ETag在重新加载的时候会被更新
,下次请求时,If-None-Match会启用上次返回的ETag值。
优先级:
ETag与If-None-Match > Last-Modified与If-Modified-Since, 同时存在时, 前者覆盖后者.
版权声明:本文为CSDN博主「楚楚梦厦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/s18438610353/article/details/121106105
本文链接: http://www.ionluo.cn/blog/posts/5dd60666.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!