浏览器强缓存与协商缓存

强缓存

强缓存和协商缓存是浏览器在处理HTTP请求时用于优化性能的两种缓存机制。

强缓存(Cache-Control和Expires) 是通过设置响应头来实现的,它告诉 浏览器在一段时间内直接使用本地缓存而不发送请求到服务器

如果资源在强缓存有效期内,浏览器将直接从本地缓存中获取资源,并且不会向服务器发送请求。

这样可以节省网络带宽和减少服务器负载。常见的响应头字段有:

  • Cache-Control:指定请求和响应遵循的缓存机制, 通过设置max-age或s-maxage指定缓存有效期。
  • Expires:响应过期的日期和时间,指定一个具体的过期日期和时间。

cache-control取值

cache-control: max-age=xxxx,public

客户端和代理服务器都可以缓存该资源;

客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,如果用户做了刷新操作,就向服务器发起http请求

cache-control: max-age=xxxx,private

只让客户端可以缓存该资源;代理服务器不缓存

客户端在xxx秒内直接读取缓存,statu code:200

cache-control: max-age=xxxx,immutable

客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,即使用户做了刷新操作,也不向服务器发起http请求

cache-control: no-cache

跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端。

cache-control: no-store

不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了。

强制缓存的思想

强制缓存的思想是,在浏览器内置数据库中缓存每次请求中 "可以被缓存" (受到一些关键字的管控)的静态资源如 image, css, js 文件, 当第二次请求被缓存过的资源时候,会通过校验两个字段 Expires 和 Cache-Control 的max-age字段(注意,Expires 是 http1.0 的产物, Cache-Control 则是 http1.1 的产物。 两者同时存在, 或者只存在其中之一, 都可以触发强制缓存。

当满足字段约束的情况下, 浏览器就不会向服务器发送请求而是直接从服务器返回数据, 同时其状态码为 200

当不满足字段约束的情况下, 浏览器则会向服务器正常发送请求

强制缓存主要取决于两个字段 Expires 和 Cache-Control 中的 max-age 字段, 在两个响应头都存在的情况下, 其流程如图

当 Expires 和 Cache-Control 字段同时存在得到时候,Cache-Control 中的 max-age 字段字段优先级会稍微高一点

当 Cache-Control 中的 max-age 字段校验成功,会直接返回浏览器内置数据库的缓存, 失效时才会将决策权传递给 Expires 字段判断。

这样设计的原因:

大概是因为 Expires 字段在设计时存在了这么一个缺陷------Expires字段返回的是服务器的时间, 而非客户端的本机时间。

当存在时差, 或者客户修改本地时间的情况下 Expires 字段会存在失效的可能性。

比如:

text 复制代码
当同一时刻下的服务器时间为 2022/4/26 06:00:00 

客户端时间为 2022/4/26 12:00:00 过期时间为两个小时之后, 则服务器会返回 2022/4/26 08:00:00 这个时间对应的值。

由于浏览器运行在客户环境下,对于客户而言, 这个缓存已经过期了,虽然缓存确实有效, 但是对于浏览器而言这个缓存确确实实是 "过期了"

这会导致强制缓存永远不会生效! 

那么为了解决Expires 字段这个问题, http 1.1 协议中添加了 Cache-Control 中的 max-age, 他是一个相对值, 即客户端获取到这个文件多少秒后失效, 其判别权力全权交由浏览器, 这会相对更准确些。

协商缓存

协商缓存 (Last-Modified和ETag)是 通过与服务器进行通信来确定资源是否需要重新下载的

当浏览器发起请求时,服务器会将资源的相关信息作为响应的一部分返回给浏览器。

浏览器在下次请求该资源时会将这些信息作为请求头的一部分发送给服务器,服务器根据这些信息判断资源是否已经发生变化。

如果资源未发生变化,服务器返回状态码304 Not Modified,告诉浏览器直接使用本地缓存。

如果资源发生了变化,服务器返回新的资源,并且带上新的相关信息。

常见的响应头字段有:

  • Last-Modified:表示资源的最后修改时间。
  • ETag:表示资源的唯一标识符。

协商缓存主要由 ETagLast-Modified 两个字段来实现.

ETag 是一个用于映射 web 资源的映射 token,这个 token 应该满足唯一对应到一 个web服务器上的静态资源(具体实现通常是提取文件相关信息进行hash和base64编码等操作)

Last-Modified 则通常是文件最后更新的日期时间戳

协商缓存的过程

浏览器首次向服务器请求数据 A, 服务器正常返回数据,同时在响应头中放入 ETagLast-Modified 两个新字段。

当浏览器第二次向服务器请求数据 A 时, 浏览器会自动地在请求头附上 If-None-Match 和 If-Modified-Since 两个字段(分别对应的是 ETag 和 Last-Modified 的值,两两相等), 然后由服务器端进行校验, 校验通过的话(表明数据有效), 服务器会直接返回 状态码 304 ,且不携带响应体的报文段, 这相当于告诉浏览器:当前缓存有效, 可以直接使用! 校验失败则会和首次请求一样, 返回状态码为200且携带数据响应体的报文段, 同时这个响应头会带上新的ETag 和Last-Modified, 为下一次协商缓存做好铺垫 。

需要注意的是, 在不用框架的情况下, 协商缓存需要由后端开发人员手动实现

因此 ETag 和 Last-Modified 两个字段的优先级取决于开发者,

但是 Last-Modified 这个字段可以记录的时间戳精确度是有一定限制的,如果连续多次数据更新在精确度范围外, 会产生精确度丢失,

因此通常会让ETag 的优先级高于 Last-Modified 字段(类似于Cache-control中max-age一样, 属于是后续改进协议的一个新字段, 因此优先级一般会高点)

强缓存协商缓存并存的情况

默认情况下,浏览器会优先考量强制缓存的情况

当强制缓存生效的情况下, 请求并不会到达服务器, 因此也就不会触发协商缓存。

当强制缓存失效的时候, 浏览器便会将请求传递到服务器, 于是服务器又会开始校验 If-Modified-Since 和 If-None-math 两个字段, 重复上述协商缓存的一个执行流程

乍一看,两者并存的情况, 有点像是两个协议的简单叠加,此时的协商缓存更像是强制缓存的兜底策略, 很可能协商缓存很长一段时间都不会生效(强制缓存过期时间设置过长的情况下),

因为强制缓存的优先级是要高于协商缓存的。

当然这并不是我们想看到的, 比方说当后端数据确实变更了, 而此时的浏览器由于使用了强制缓存,则会出现数据不一致的情况,

因此在这里引入了 请求头中的两个字段 no-cache, 当使用了 no-cache 字段的时候, 浏览器将不再使用强制缓存, 而是直接去请求服务器,

这个时候就会用到协商缓存了(顺带一提的是, 还有一个 no-store 字段, 用了这个字段浏览器则不会在使用缓存的数据也不缓存数据,即强制缓存和协商缓存都失效了)

总结

强制缓存在缓存有效的情况下不会去请求服务器, 其数据来源则是浏览缓存的本地磁盘。

而协商缓存会向服务器请求,但是在协商缓存成功的情况下, 服务器只会返回一个不带响应体的报文,结合开头的背景来说强制缓存选择"减少过桥次数"的策略, 而协商缓存则是采用 '减少过桥人数'的策略

强制缓存在浏览器强制刷新的情况下不会生效, 而协商缓存则不受影响。(调试代码测试时候,要注意)

强制缓存返回的报文状态码为 200, 协商缓存返回的报文状态码为 304 (前端使用fetch请求的情况, 协商缓存的 状态码304 会转成 200)

强制缓存发生在浏览器端, 协商缓存发生在服务器端

当浏览器首次请求一个网页资源时,服务器可以通过设置强缓存和协商缓存来优化浏览器的缓存策略。

强缓存和协商缓存可以结合使用,首先通过强缓存判断是否可以从本地缓存获取资源,如果无法命中强缓存,则进行协商缓存验证。

这样可以更加灵活地控制缓存策略,提高网页加载速度和用户体验。

实例

强缓存的例子:

  1. 使用Cache-Control设置max-age:

    Cache-Control: max-age=3600
    

    这表示该资源在请求后的3600秒(1小时)内有效。在这段时间内,浏览器直接从本地缓存中获取资源,而不向服务器发送请求。

  2. 使用Expires设置过期日期:

    Expires: Fri, 01 Jan 2024 00:00:00 GMT
    

    这表示该资源在过期日期之前都是有效的。'

    在过期日期之前,浏览器直接使用本地缓存。

协商缓存的例子:

  1. 使用Last-Modified和If-Modified-Since进行资源验证:

    当浏览器首次请求资源时,服务器会返回响应头中的Last-Modified字段,表示资源的最后修改时间。

    Last-Modified: Wed, 20 Sep 2023 10:00:00 GMT
    

    当下次请求资源时,浏览器会将If-Modified-Since字段带上,值为上一次请求时服务器返回的Last-Modified值。

    If-Modified-Since: Wed, 20 Sep 2023 10:00:00 GMT
    

    服务器比较资源的最后修改时间与If-Modified-Since字段的值,如果相同则返回304 Not Modified,浏览器直接使用本地缓存;如果不同则返回新的资源。

  2. 使用ETag和If-None-Match进行资源验证:

    当浏览器首次请求资源时,服务器会返回响应头中的ETag字段,表示资源的唯一标识符。

    ETag: "abcdefg"
    

    当下次请求资源时,浏览器会将If-None-Match字段带上,值为上一次请求时服务器返回的ETag值。

    If-None-Match: "abcdefg"
    

    服务器比较资源的ETag与If-None-Match字段的值,如果相同则返回304 Not Modified,浏览器直接使用本地缓存;如果不同则返回新的资源。

如何用

像目前用vue-cli打包后生成的单页文件是有一个html,与及一堆js css img资源,怎么去设置这些文件呢,核心需求是

  • 要有缓存,毋庸置疑
  • 当发新包的时候,要避免加载老的缓存资源

做法是:

index.html文件采用协商缓存

理由就是要用户每次请求index.html不拿浏览器缓存,直接请求服务器,这样就保证资源更新了,用户能马上访问到新资源,如果服务端返回304,这时候再拿浏览器的缓存的index.html,

切记不要设置强缓存!!!

其他资源采用强缓存 + 协商缓存,理由就不多说了。

怎么设置强缓存与协商缓存

后端服务器如nodejs

js 复制代码
res.setHeader('max-age': '3600 public')
res.setHeader(etag: '5c20abbd-e2e8')
res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT) 

nginx配置

参考文档:

相关推荐
wkj001几秒前
php操作redis
开发语言·redis·php
武子康3 分钟前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康5 分钟前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql
极客代码6 分钟前
【Python TensorFlow】进阶指南(续篇三)
开发语言·人工智能·python·深度学习·tensorflow
苏-言11 分钟前
Spring IOC实战指南:从零到一的构建过程
java·数据库·spring
土豆湿11 分钟前
拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流
开发语言·javascript·css
界面开发小八哥18 分钟前
更高效的Java 23开发,IntelliJ IDEA助力全面升级
java·开发语言·ide·intellij-idea·开发工具
菠萝咕噜肉i31 分钟前
超详细:Redis分布式锁
数据库·redis·分布式·缓存·分布式锁
草莓base31 分钟前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
Allen Bright1 小时前
maven概述
java·maven