浏览器http缓存机制

article/2025/9/11 5:13:09

1、前言

前端缓存主要是分为HTTP缓存浏览器缓存。其中HTTP缓存是在HTTP请求传输时用到的缓存,主要在服务器代码上设置;而浏览器缓存则主要由前端开发在前端js上进行设置。
在这里插入图片描述
http缓存是web缓存的核心,是最难懂的那一部分,也是最重要的那一部分。

2、HTTP缓存位置

从缓存位置上来说分为四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。

  • Service Worker
  1. 行在浏览器背后的独立线程,一般可以用来实现缓存功能
  2. 因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS
    协议来保障安全
  3. Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
  • Memory Cache
  1. 内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。
  2. 读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。
  3. 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了
  4. 访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存
  • Disk Cache
  1. Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点
  2. Disk Cache 比Memory Cache胜在容量和存储时效性上
  3. 在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的
  4. 它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求
  • Push Cache
  1. Push Cache是推送缓存,是 HTTP/2 中的内容
  2. 只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂
  3. 在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。

如果以上四种缓存都没有命中的话,那么只能发起请求来获取资源了。

那么为了性能上的考虑,大部分的接口都应该选择好缓存策略,通常浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。

3、HTTP缓存-强缓存

对于强制缓存而言,如果浏览器判断所请求的目标资源有效命中,则直接从强制缓存中返回请求响应,无须与服务器进行任何通信。返回200的状态码。

在浏览器控制台NetWork中的体现为:
200 OK (from disk cache) 或者 200 OK (from memory cache)

  • 200 OK (from disk cache) HTTP状态码200,缓存的文件从硬盘中读取
  • 200 OK (from memory cache) HTTP状态码200,缓存的文件从内存中读取

强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control。

3.1基于expires实现
request header: Last-modified
response header: expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。需要和Last-modified结合使用。Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。

  • HTTP1.0协议中声明的用来控制缓存失效日期时间戳的字段
  • 由服务器端指定后通过响应头告知浏览器,浏览器在接收到带有该字段的响应体后进行缓存。
  • 如果浏览器再次发起相同的资源请求,便会对比expires与本地当前的时间戳。
    • 当前请求的本地时间戳小于expires的值,则说明浏览器缓存的响应还未过期,无须向服务器端再次发起请求
    • 当本地时间戳大于expires值,缓存过期,重新向服务器发起请求。(仅过期才能允许再次发送请求)
  • expires判断的局限性
    • 对本地时间戳过分依赖
    • 如果客户端本地的时间与服务器端的时间不同步,或者对客户端的时间进行主动修改,那么对于缓存过期的判断可能就无法和预期相符
  • 解决expires判断的局限性:HTTP1.1协议开始新增了cache-control字段

3.2基于cache-control实现
在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存。比如当Cache-Control:max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。

Cache-Control:max-age=N,N就是需要缓存的秒数。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从磁盘(或内存中读取),不与服务器做任何交互。

Cache-control中因为max-age后面的值是一个滑动时间,从服务器第一次返回该资源时开始倒计时。所以也就不需要比对客户端和服务端的时间,解决了Expires所存在的巨大漏洞。

基于cache-control实现的强缓存是当下项目中的常规方法,而基于expires实现的强缓存不被推荐使用。

Cache-Control 可以在请求头或者响应头中设置,并且可以组合使用多种指令

  1. max-age:(单位为s)表示响应资源能被缓存多久
    • max-age和expires同时存在,则以max-age为准
  2. s-maxage:(单位为s)缓存在代理服务器中的过期时长(仅当设置了public属性值时才是有效的)
    • max-age和s-maxage并不互斥。他们可以一起使用
  3. no-cache:强制进行协商缓存
    • Cache-control中设置了no-cache,那么该资源会直接跳过强缓存的校验,直接去服务器进行协商缓存
  4. no-store:禁止使用任何缓存
    • 客户端的每次请求都需要服务器端给予全新的响应
    • no-cache与no-store是两个互斥的属性值,不能同时设置
  5. public:表示响应资源既可以被浏览器缓存,又可以被代理服务器缓存
  6. private:限制了响应资源只能被浏览器缓存
    • public和private就是决定资源是否可以在代理服务器进行缓存的属性
    • 如果这两个属性值都没有被设置,则默认为private
    • public和private 也是一组互斥属性

Cache-control如何设置多个值呢?用逗号分割

Cache-control:max-age=10000,s-maxage=200000,public

如果要考虑向下兼容的话,在Cache-control不支持的时候,还是要使用Expires,这也是我们当前使用的这个属性的唯一理由。

3.3强缓存两种方式的区别

  • Expires 是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间,可能会造成缓存失效
  • Expires 是http1.0的产物,Cache-Control是http1.1的产物
  • expires/cache-control两者同时存在的话,Cache-Control优先级高于Expires
  • 在某些不支持HTTP1.1的环境下,Expires就会发挥用处
  • Expires其实是过时的产物,现阶段它的存在只是一种兼容性的写法

3.4强缓存的缺陷
强缓存判断是否缓存的依据来自于是否超出某个时间或者某个时间段,而不关心服务器端文件是否已经更新,这可能会导致加载文件不是服务器端最新的内容,那我们如何获知服务器端内容是否已经发生了更新呢?此时我们需要用到协商缓存策略。

4、HTTP缓存-协商缓存

顾名思义,协商缓存就是在使用本地缓存之前,需要向服务器发起一次GET请求,与之协商当前浏览器保存的本地缓存是否已经过期。通常是采用所请求资源的最近一次的修改时间戳来判断的。

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:

  • 协商缓存生效(文件未更新),返回304和Not Modified。

  • 协商缓存失效(文件更新),返回200和请求结果。

协商缓存可以通过设置两种 HTTP Header 实现:Last-Modified 和 ETag 。

4.1基于last-modified实现

  1. 首先需要在服务器端读出文件修改时间
  2. 将读出来的修改时间赋给响应头的last-modified字段
  3. 最后设置Cache-control:no-cache

第一次请求,服务端代码

 const data = fs.readFileSync('./imgs/CSS.png');//读取资源// 1.读取修改的时间const { mtime } = fs.statSync('./imgs/CSS.png');// 2.设置文件最后修改时间res.setHeader('last-modified',mtime.toUTCString())// 3.强制设置为协商缓存res.setHeader('Cache-Control','no-cache');res.end(data);

第二次及以后的每一次请求,服务器端代码

 const data = fs.readFileSync('./imgs/CSS.png');//读取资源const { mtime } = fs.statSync('./imgs/CSS.png');//读取修改的时间const ifModifiedSince = req.headers['if-modified-since'];//读取请求头携带的时间(第一次返回给客户端的文件修改时间)if (ifModifiedSince === mtime.toUTCString()) {// 如果两个时间一致,则文件没被修改过,返回304res.statusCode = 304;res.end();//因为缓存生效,不需要返回数据return;// 避免返回新的last-modified}res.setHeader('last-modified',mtime.toUTCString())// 设置文件最后修改时间res.setHeader('Cache-Control','no-cache');// 强制设置为协商缓存res.end(data);

流程

  • 客户端第一次请求目标资源的时,服务器返回的响应标头包含last-modified字段,值是该资源的最后一次修改的时间戳,以及cache-control:no-cache
  • 当客户端再次请求该资源的时候,会携带一个if-modified-since字段,其值正是上次响应头中last-modified的字段值
  • 如果客户端请求头携带的if-modified-since字段对应的时间与目标资源的时间戳进行对比,如果没有变化则返回一个304状态码。
  • 如果有变化则返回最新的last-modified标头和Cache-Control:no-cache

last-modified的不足

  • last-modified是根据请求资源的最后修改时间戳进行判断的,虽然请求的文件资源进行了编辑,但是内容并没有发生任何变化,时间戳也会更新,从而导致协商缓存时关于有效性的判断验证为失效,需要重新进行完整的资源请求。浪费了网络的带看资源,延长获取资源的时间
  • 标识文件资源修改的时间戳单位是秒,如果文件修改的速度非常快,假设在几百毫秒内完成,那么通过时间戳的方式来验证缓存的有效性,是无法识别出该次文件资源的更新的。

综合,以上两种不足可知,基于last-modified实现的协商缓存,服务器无法根据资源修改的时间戳识别出真正的更新,进而导致重新发起了请求

4.2基于Etag实现
为了弥补通过时间戳判断的不足,从HTTP1.1规范开始新增了一个Etag的头信息,即实体标签。 其内容主要是服务器为不同的资源进行哈希计算所生成的一个字符串,该字符串类似于文件指纹,只要文件内容编码存在差异,对应的Etag对文件资源进行更精准的变化感知。

也就是说,ETag就是将原先基于last-modified协商缓存的比较时间戳的形式修改成了比较文件指纹。

文件指纹:根据文件内容计算出的唯一哈希值。文件内容一旦改变则指纹改变。

服务端代码:
在这里插入图片描述
流程

  • 第一次请求资源时,服务端将要返回给客户端的数据通过ETag模块进行哈希计算生成一个字符串,这个字符串类似于文件指纹。
  • 第一次请求资源时,服务端将要返回给客户端的数据通过ETag模块进行哈希计算生成一个字符串,这个字符串类似于文件指纹。
  • 检测客户端的请求标头中的if-None-Match字段的值和第一步计算的值是否一致,一致则返回304。
  • 如果不一致则返回etag标头和Cache-Control:no-cache

etag的不足

在协商缓存中,Etag并非last-modified的替代方案而是一种补充方案,因为依旧存在一些弊端。

  • 服务器对于生成文件资源的Etag需要付出额外的计算开销,如果资源的尺寸比较大,数量较多且修改频繁,那么生成的Etag的过程就会影响服务器的性能。
  • Etag字段值的生成分为强验证和弱验证,强验证根据资源内容进行生成,能够保证每个字节都相同,弱验证则根据资源的部分属性来生成,生成速度快但无法确保每个字节都相同,并且在服务器集群场景下,也会因为准确不够而降低协商缓存有效性的成功率,所以恰当的方式是根据具体的资源使用场景选择恰当的缓存校验方式。

4.3Etag/last-modified对比

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

5、怎么设置缓存

  • 普遍服务器默认开启强缓存
  • 缓存是缓存在前端,但实际的代码是后端同学写的,如果需要实现前端缓存的话,通知后端的同学加响应头就好了。

http://chatgpt.dhexx.cn/article/lSje72vP.shtml

相关文章

Http缓存机制与原理

一 Http缓存基本概念 1.1 Http报文 在浏览器和服务器进行Http通信时发送的数据即为Http报文,其中分为两部分: header - 报文的首部或头部,其中保存着各类请求的属性字段,关于Http的缓存相关规则信息均保存在header中body - 请求…

前端基础-浏览器缓存/HTTP缓存机制(面试常考)

文章目录 一、HTTP报文1.HTTP请求(Request)报文2.HTTP响应(Response)报文 二、缓存过程分析三、缓存规则1.强制缓存1.1Expires1.2 Cache-Control1.3例子 2. 缓存存储3.协商缓存3.1 Last-Modified / If-Modified-Since3.2 Etag / If-None-Match 四、不同刷新的请求执行过程五、总…

一文理解http缓存机制

HTTP报文 浏览器的缓存机制也就是我们所说的HTTP缓存机制,是根据HTTP报文的缓存标识进行的。先了解下HTTP报文: 请求报文 报文格式:请求行 – 请求头(通用信息头,请求头,实体头) – 请求体(只有POST才有请求体) 响…

http缓存机制

http缓存机制 1. 什么是缓存2. 缓存主要目的3. http缓存概述3.1 强缓存3.2 协商缓存 1. 什么是缓存 缓存(cache)是数据交换的缓冲区,是临时存储数据的仓库,在有大量数据交换的应用程序中,我们会采取一些方式将那些实时…

HTTP缓存机制及原理详解(最全)

前言 缓存技术是无数WEB开发从业人员在工作过程中不可避免的一大问题。在产品开发的时候我们总是想办法避免缓存产生,而在产品发布之时又在想策略管理缓存提升网页的访问速度。了解浏览器的缓存命中原理,是开发WEB应用的基础,本文着眼于此&a…

HTTP缓存机制详解

HTTP缓存机制详解 一. 前言二. 缓存的介绍什么是缓存?为什么要使用缓存?1. 减少冗余的数据传输2. 缓解带宽瓶颈3. 破坏瞬间拥塞4. 降低距离时延 三. 缓存有效性命中和未命中的再验证命中率字节命中率区分响应来自缓存还是服务器 四. 缓存拓扑结构私有缓存…

redis消息订阅与发布

一、消息订阅与发布 消息的订阅和发布是进程间的一种消息通信模式,发送者(pub)发送消息,订阅者(sub)接收消息。 二、常用命令示例 先订阅后发布,才能收到消息 执行SUBSCRIBE可以一次性订阅多个 执行PUBLISH命令,发布消息 …

redis发布与订阅

一、什么是发布和订阅 发布订阅是一种应用程序(系统)之间通讯,传递数据的技术手段。特别是在异构(不同语言)系统之间作用非常明显。发布订阅可以是实现应用(系统)之间的解耦合。 发布订阅&…

redis的发布和订阅

1、什么是发布和订阅 redis发布订阅(pub/sub)是一种消息通信模式:发布者(pub)发布消息,订阅者(sub)接收消 息。 redis客户端可以订阅任意数量的频道。 2、redis的发布和订阅 1、客户…

Redis——Redis 的发布和订阅

Redis 的发布和订阅 文章目录 Redis 的发布和订阅1、什么是发布和订阅2、Redis 的发布和订阅3、发布订阅命令行实现 1、什么是发布和订阅 Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。 Redis 客户端可…

Redis如何实现发布订阅功能

Redis如何实现发布订阅功能 文章目录 Redis如何实现发布订阅功能前言一、Redis发布订阅功能1. 发送消息2. 订阅某个频道3. 发布订阅的实现4. 总结 前言 Redis提供了发布订阅功能,可以用于消息的传输,Redis的发布订阅机制包括三个部分,发布者…

Java实现Redis的发布和订阅

Redis上的操作: 打开两个窗口,打开redis客户端: 一个客户端:订阅(客户端订阅channel1频道):127.0.0.1:6379> subscribe channel1 另一个客户端:发布(客户端向channel1频道发送消…

redis 发布订阅以及使用场景

redis 发布订阅 发布订阅模式:一个发布者多个订阅者只要选择订阅这个发布者,发布者发布的数据都可以被订阅到,只有订阅者开始订阅之后,发布的数据才可以接收,也就是说历史数据不能接收 127.0.0.1:6379> PUBLISH 163 hello (i…

Redis---订阅和发布

目录 消息系统命令 消息系统 ​ 发布/订阅,即 pub/sub,是一种消息通信模式:发布者也称为消息生产者,生产和发送消息到存储系统;订阅者也称为消息消费者,从存储系统接收和消费消息。这个存储系统可以是文件系…

Redis:Redis消息的发布与订阅(了解)

为了实现客户端的通信,提供了频道的概念 1.Redis发布订阅 Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis 客户端可以订阅任意数量的频道。 Redis发布订阅示意图 图一:消息订阅者…

Spring boot整合Redis实现发布订阅(超详细)

Redis发布订阅 基础知识相关命令订阅者/等待接收消息发布者/发送消息订阅者/成功接收消息常用命令汇总 原理Spring boot整合redis导入依赖Redis配置消息封装类(MessageDto)Redis配置类测试类订阅方实现一:RedisMessageListener订阅方实现二&a…

【redis】发布和订阅消息

1.说明 在Redis2版本之后支持发布订阅功能,发布者创建一个频道,并在上面发送消息,所有订阅该频道的客户端都能收到消息(不出意外的情况下,但实际不一定),发布订阅的好处是减少不必要的轮询&…

Redis的发布订阅模式

本文源码参看:https://github.com/duktig666/learn-example/tree/5586febea31c2fb368e19fbdba11ed08afd463e0/Redis/src/main/java/cn/duktig/pubsub Redis发布订阅概述 Redis 发布订阅 (publish/subscribe) 是一种消息通信模式:发送者 (pub) 发送消息…

Redis订阅和发布

1.发布和订阅 1.1什么是发布和订阅 发布订阅是一种应用程序(系统)之间通讯,传递数据的技术手段。特别是在异构(不 同语言)系统之间作用非常明显。发布订阅可以是实现应用(系统)之间的解耦合。…

Redis数据库的订阅发布

大家好,今天分享一下redis的订阅发布 Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。 Redis 客户端可以订阅任意数量的频道。 (比如说,你在一个一个网站上面可以关注…