HTTP 缓存,拿捏🤏
Lazy loaded imageHTTP 缓存,拿捏🤏
2024-11-11
| 2024-12-30
字数 7768阅读时长 20 分钟
type
status
date
slug
summary
tags
category
icon
password
 
🔥

前言

写本文也是因为在工作中遇到了太多和用户沟通例如 ”你在页面上刷新一下”(一串丝滑小连招)清空缓存然后当作无事发生”换个浏览器试一下” 的无奈。而用户和开发者之间的认知也存在差异,不懂得在不同页面上刷新的区别,沟通起来十分不顺畅。
希望阅读完本文,你以后都可以有底气的告诉对方,亲,退出页面重新进一下,这个bug已经修复了!
 
⚠️
缓存在各个领域都有应用,本文主要关注 HTTP 缓存。

TL;DR

给所有静态资源的响应头都添加上Cache-Control: no-cache ,保证你再也不受缓存的折磨!想要更系统性的了解缓存,请阅读下文~
 

历史

最早,浏览器是不做缓存的,此时对开发者负担最小,完全不用考虑一个bug可能由缓存引起。
 
后来,随着HTTP标准的完善,浏览器逐步添加了一些默认的缓存机制并不断微调策略。导致在一些情况下,当开发者没有控制缓存时会导致用户遇到缓存问题。(虽然最新的 HTTP 标准 中定义 缓存属于可选项,但是站在浏览器的角度,由于缓存收益较大,基本上浏览器都会默认开启)
💡
你知道吗?
最早的缓存标准可以追溯到1997年的 RFC2068 (或许有更早的?) 而Chromium项目(chrome浏览器的内核)是2008年起步的。初版提交的代码就实现了标准中提到的缓存功能。
 
自此,开发者需要了解规范、具体浏览器的实现,以正确使用缓存,负担明显加重。
 
⚠️
缓存的负担
对开发者能力有要求,要能够驾驭缓存。否则会出现包括但不限于以下问题:
  • 用户总是访问旧版本带来的一系列问题,如页面 UI 异常、功能异常、接口异常、漏洞修复无法及时推送等
  • 不同用户(区域、设备)体验不一致
  • 。。。

缓存的价值是什么

💡
缓存机制通常不为开发者带来直接受益,而更多在于运维、用户侧。
  • 减轻服务器带宽负担
  • 加快用户访问速度
  • 补充离线/弱网场景体验
  • 。。。

下文结构

 
第一部分:概念、机制讲解
 
先讲讲缓存标准。
然后讲讲缓存客户端(浏览器作为典型客户端讲解)对缓存标准的实现(对标准的实现程度,标准中开放性、建议的部分的具体措施,例如 标准建议将缓存控制权交给用户,浏览器是如何实现的
 
第二部分:开发者实践
首先会教你如何重置浏览器的默认缓存机制,使我们上线的页面回归到无缓存状态。(简单一刀切,彻底杜绝缓存原因)
 
然后介绍如何渐进式地添加缓存,以发挥缓存的价值
 
最后基于实际场景,分析解决缓存带来的问题,巩固认知。
 

第一部分:概念、机制

让我们开始揭开缓存的面纱吧

1. 首先,什么是缓存?

缓存的定义, 参考 RFC9111 对其的定义:
An HTTP "cache" is a local store of response messages and the subsystem that controls storage, retrieval, and deletion of messages in it. HTTP“缓存”是响应消息的本地存储,以及控制其中消息的存储、检索和删除的子系统。
💡
下文中,“缓存” 一词代表存储还是调度程序请根据具体的上下文判断!请先记住这个概念。

2. 先对现代浏览器缓存有个基本认知

标准、浏览器应用、操作系统。。。哪一方对缓存有绝对控制权?
 
操作系统负责给上层应用(浏览器)分配资源(储存空间、内存、CPU等),缓存虽然以文件形式持久化保存,但是对操作系统而言,他就是个文件,和其他文件没有区别。通常无法进行针对性识别。
 
浏览器自身当然可以(不顾标准)随意实现包括缓存在内的各种功能,但是为了更好地提升影响力,通常会最大限度地采用标准(权威)。
 
HTTP缓存标准中对缓存进行了全方位的阐述,浏览器根据标准进行实现,我们阅读标准来全面、框架地掌握浏览器的实际行为。
 
  1. 重装操作系统能清除缓存吗?
  1. 重装浏览器/换个浏览器呢?
  1. 关闭浏览器重开能清除缓存吗?
  1. 无痕模式呢?
 
钻牛角尖来讲的话,针对1,可以修改浏览器保存缓存的位置,转移到非系统盘,从而实现即使重装了操作系统都保留缓存;针对2也同理,但是换个浏览器是肯定能避免受到缓存的影响的。因为不同应用通常不会将配置存储在同一个地方。
3. 从浏览器的视角讲,当资源被判定为新鲜时,他就会一直重用缓存。除非硬刷新、清除缓存, 每个浏览器应用都管理各自的缓存。且是持久化为文件形式的。所以重开浏览器通常不会清除缓存。
4.无痕模式属于浏览器自己自由发挥的功能,其使用一个临时的隔离的沙箱空间,有缓存机制,但是和正常模式是隔离的。
 
但是从全局视角来讲,缓存除了本地会留,还可能会在中间介质(intermediary)上留存,例如CDNNginx。这时即使你换了一台电脑,如果中介上的缓存没有配置正确的刷新,客户端依然会下载旧的资源。
 

3. 洞悉标准

术语解释
共享缓存/shared cache: 用于将缓存响应给多个用户,例如CDN
私有缓存/private cache :响应单个用户,通常是浏览器程序中的一个组件
 
缓存状态: 新鲜(fresh)/ 过期(stale)/ 离线(disconnected
每条缓存都存在新鲜生命周期(保质期),没过期就是新鲜的。保质期主要由响应头确定。 当没有合适的响应头时,缓存可能会被设定一个基于启发式规则的缓存时间
新鲜缓存表示当前缓存可以不用被重新验证就直接使用;
过期缓存表示当前缓存在被重用之前必需经过验证。
⚠️
特殊情况disconnected
标准(#section-4.2.4)中提到:
 
A cache MUST NOT generate a stale response if it is prohibited by an explicit in-protocol directive (e.g., by a no-cache response directive, a must-revalidate response directive, or an applicable s-maxage or proxy-revalidate response directive; see Section 5.2.2).
如果协议指令明确禁止(例如,通过 no-cache 响应指令、must-revalidate 响应指令或适用的 s-maxageproxy-revalidate 响应指令;见 第 5.2.2 节),缓存 不得 生成过时的响应。
 
 
A cache MUST NOT generate a stale response unless it is disconnected or doing so is explicitly permitted by the client or origin server (e.g., by the max-stale request directive in Section 5.2.1, extension directives such as those defined in [RFC5861], or configuration in accordance with an out-of-band contract).
缓存 不得 生成过时的响应,除非它已断开连接或客户端或源服务器明确允许这样做(例如,通过 第 5.2.1 节 中的 max-stale 请求指令、如 [RFC5861] 中定义的扩展指令,或根据带外合同的配置)。
 
也就是说,在一些情况下,在设备离线时缓存也依然可以用于响应。
 
新鲜度生命周期/Freshness Lifetime:当资源进入缓存后,缓存在特定时间周期内遇到命中的请求内不再访问服务器而直接充当响应, 这个时间周期就是这个缓存条目的新鲜度生命周期。
从定义上来讲,它代表具体的缓存条目由源服务器生成到过期时间之间的时间长度。
 
验证器/validator 缓存不再新鲜后,重新验证其条目有效性的一个程序。 通常结合请求头响应头中的特定头判断。详细的缓存验证请参考 缓存验证
 
标准主要有 RFC 9111:HTTP Caching、RFC 9110:HTTP Semantics
💡
你知道吗?
缓存相关 RFC 经历了从 RFC2068 RFC2616RFC7234再到最新的RFC9111。最新的RFC9111为2022年6月出版。
 
RFC9111为缓存(存储形式+缓存管理系统)提供了设计指南,约束。 最需要看的是(想要走得长远的)浏览器内核开发者,其次是想掌握浏览器机制的网页开发者。
 
首先,通常只有GETHEAD(工作中接触较少,暂不展开)请求可以被浏览器缓存。POST响应常规情况下是不缓存的 规范中定义的可启发式缓存的响应状态码才能被缓存。
 
然后,如果响应没有附带缓存相关的响应头,则浏览器可以自行为响应分配一个“过期时间”,这个机制称为启发式规则的缓存(heuristic cache)
 
启发式缓存的常规算法: 新鲜生命周期 = (当前时间 - 资源上次修改的时间) * 0.10
💡
关于启发式缓存的详细分析请查阅下文启发式缓存的标准及实现
 
启发式缓存会随着资源待在服务器上越久没修改而缓存得越来越久(当客户端请求时),而且存在一个很致命的问题:老用户通过导航进入页面时,如果没有超过启发式缓存的新鲜度生命周期,则不会触发缓存验证。也就是说即使这时站长提交了资源更新,老用户不管怎么导航访问也无法接收到!而即使用户之后刷新了页面,也有可能无法完全获取到新的内容。(因为chromium重载页面只会强制验证主资源,子资源(例如jscss等文件)根据之前的缓存规则来进行验证)
 
💡
可以简单的认为 导航 是指用户通过跳转的方式打开页面,之前目标页面是没有打开的;而刷新(也叫重载)则是在当前页面已打开的前提下进行的。 具体概念会在下文讲述。
 
 
所以,为了让缓存更可控,通常不应该让资源落入启发式缓存的范畴内。
 
那么, 有哪些手段可以逃离这个默认设定呢?
标准中有提到:
A cache MUST NOT use heuristics to determine freshness when an explicit expiration time is present in the stored response.Because of the requirements in Section 3, heuristics can only be used on responses without explicit freshness whose status codes are defined as "heuristically cacheable" (e.g., see Section 15.1 of [HTTP]) and on responses without explicit freshness that have been marked as explicitly cacheable (e.g., with a public response directive). 当存储的响应中存在显式过期时间时,缓存绝对不能使用启发式方法来确定新鲜度。由于第3节的要求,启发式只能用于状态码被定义为“启发式可缓存”的无显式新鲜度的响应(例如,参见[HTTP]的第15.1节),以及标记为显式可缓存的无显式新鲜度的响应(例如,使用公共响应指令)。
 
答案就是给 HTTP响应 添加一个明确的过期时间或者显式定义新鲜度,浏览器的缓存组件会识别到响应头信息,在再次有请求命中该缓存条目时,就不再以启发式规则判断缓存是否可重用,而是转为根据缓存响应头中显式定义的过期时间/新鲜度规则来决定是否直接响应
💡
你知道吗?
缓存组建处于HTTP请求的上层,对请求而言几乎是透明的。即使是对新网站的第一次请求,依然会判断缓存中是否有对应的条目(当然此时不会命中)
 
而过期时间、新鲜度主要由 Cache-ControlExpires(已过时))头设定。Cache-Control官方支持的所有指令定义在IANA文档中:
Directives
Desc
Example
max-age
响应的年龄超过指定秒数后,被视为失效(stale) ps. 年龄会参考Age响应头
max-age=5
must-revalidate
缓存记录在过时(stale)后, 在验证成功之前不得重用于满足请求
该指令不带参数
must-understand
no-cache
该条缓存记录在验证成功之前绝对不可以用来响应请求
通常不带参数,可以附加一个参数列表
no-store
缓存绝对不可以存储请求或响应得任何部分,也绝对不可以使用改响应来满足请求
该指令不带参数
no-transform
private
共享缓存不得存储该响应,以及私有缓存可以存储该响应
/
proxy-revalidate
public
缓存可以存储响应,即使在特殊情况下被禁止(Authoritarian头)
s-maxage
(用于共享缓存,不讲)
immutable
标识在新鲜度生命周期内不会更新?
stale-if-error
缓存在遇到错误(例如,500 内部服务器错误、网络中断或 DNS 故障)时返回一个过时的响应,而不是返回一个“硬”错误。
stale-while-revalidate
缓存立即返回一个过期的响应,同时在后台重新验证它,从而隐藏从客户端看来的延迟(包括网络和服务器上的延迟)
only-if-cached(request header)
max-stale(request header)
min-fresh(request header)

4. 了解一下各大网站的实践!

vueuse官网资源
vueuse官网资源
浙里办官网资源,没有使用构建,且仅有少数资源加了缓存头,也许面临着缓存问题
浙里办官网资源,没有使用构建,且仅有少数资源加了缓存头,也许面临着缓存问题
维基百科,对资源有着复杂、细粒度的的缓存控制
维基百科,对资源有着复杂、细粒度的的缓存控制
百度百科,关键资源24小时内保新
百度百科,关键资源24小时内保新
 
综上,可以看看到,开发者之间对于缓存的认知是存在较大差异的。当然,缓存也应该根据网站的特点有针对性地设置,一套方案走遍天往往不是最优解。
 
💡
你知道吗?
HTTP3标准中含有 QPACK的实现,其根据当时互联网中常见的响应头做了排名,将最热门的响应头字符串进行编码形成了一个表格,以表格对照的形式进行响应头的压缩。参照表格可以得知近年流行的响应头组合。
 

5. 浏览器内核对缓存标准的实现

绝大多数标准中的约束都被浏览器实现了,但是标准并不是全都是约束,依然存在很多留白、建议性质的描述。这部分就看客户端的发挥了。

标准中有哪些留白?

  • 缓存存储的介质
  • 启发式缓存的处理办法
  • 用户代理允许最终用户控制缓存
 
  • 缓存键的实现(偏底层,不讲)
  • 安全考虑(偏题,不讲)
  • 分段请求和响应的缓存(不常用,不讲了)
  • 。。。

缓存存储的介质

disk cache、memory cache
notion image
 
disk cache 、 memory cache 是如何切换的?
第一次访问网站,正常读取所有内容并缓存。
关掉,第二次访问,则从 disk 中读取缓存。
刷新,第三次访问,则从 memory 中读取。
图片 js等会直接存入内存中,而 css 会存入硬盘中。
该部分属于浏览器为了提升用户体验而优化的功能,并非长期不变。描述的逻辑仅供参考
 
浏览器的缓存存储在哪个地方?
chrome://version/

启发式缓存的标准及实现

标准中关于启发式缓存有提到如下:
Since origin servers do not always provide explicit expiration times, a cache MAY assign a heuristic expiration time when an explicit time is not specified, employing algorithms that use other field values (such as the Last-Modified time) to estimate a plausible expiration time. This specification does not provide specific algorithms, but it does impose worst-case constraints on their results.
由于源服务器并不总是提供明确的过期时间,当未指定明确过期时间时,缓存可以分配一个启发式过期时间,使用其他字段值(例如最后修改时间)来估算一个合理的过期时间。本规范并未提供具体的算法,但对其结果施加了最坏情况的限制。
 
If the response has a Last-Modified header field (Section 8.8.2 of [HTTP]), caches are encouraged to use a heuristic expiration value that is no more than some fraction of the interval since that time. A typical setting of this fraction might be 10%.
如果响应具有 Last-Modified 头字段([HTTP] 的第 8.8.2 节),则建议缓存使用一个启发式过期值,该值不超过自该时间以来的某个时间间隔的某个分数。这个分数的典型设置可能是 10%。
 
💡
chromium 对于启发式缓存的实现
结合注释读代码,可以得知,chromium内核在响应头包含有效的 Last-Modified 头时,其启发式规则将缓存时间设置为当前时间(优先取响应头的Date,也就是服务器时间,响应头不含Date则取宿主机本地时间)和目标资源的最后修改时间的差值的10%。 举例:如果js文件上次修改时间是365天之前,则客户端初次加载资源后,在接下来的36.5天内(365 * 10%),都会跳过缓存验证。
 
🔥
实践验证(精准预判启发式缓存触发时机,成就感MAX!!!)
自行搭建环境,不复杂。
 

如何通过浏览器界面控制缓存

术语解释
用户代理/UA :拥有一个网址,如何呈现一个页面?需要经过DNS解析、请求头构建、TCP连接建立、获取到响应、渲染响应结果等等步骤。这些复杂的流程都被应用程序处理了,我们通过适当的应用程序(例如浏览器),只需要输入网址,就可以看到页面。负责 代为用户处理一系列工作 的程序就称为 用户代理 。 在浏览网页这件事情上,浏览器程序本身就是用户代理。
 
重载/Reload = 刷新(Refresh/soft reload): 重新验证 主资源 缓存(最新的chromium内核在重载时不再验证子资源缓存)
硬刷新(Hard Refresh):不使用缓存中的任何资源发出请求
清空缓存并硬刷新:在硬刷新之前 先清空当前站点缓存
上述三者的区别引用于
 
导航/Navigating :从用户发出URL请求到页面开始解析的过程称为导航
导航时标签页的加载中图标区别于解析阶段:chrome目前为倒转的灰色的loading;edge目前就是灰色的loading
 
几种用户行为的区别
点击刷新按钮 / F5 / Ctrl+R
重新验证主资源(通常是入口文档资源),会为主资源添加 max-age=0 的 请求头并强制触发重新验证。 子资源根据其响应头原来的缓存规则来。
 
Ctrl + Shift + R / 打开开发者工具之后右键刷新按钮点击硬刷新 / 开发者工具→ 网络面板 → 勾选禁用缓存 后通过上述方式刷新
发出的请求不使用任何缓存,强制浏览器重新下载所有的资源文件。
 
导航
相比刷新,主资源也会遵循缓存规则
⚠️
如果已经有打开了目标页面的标签页,则再通过新的标签页/窗口打开目标页面,其资源会被视为刷新。
 
设置中删除缓存 → 重新访问页面 / 打开开发者工具栏 → 右键刷新按钮 → 清空缓存并硬刷新
这个就不用解释了吧。
notion image
 
 

6. 浏览器的其他缓存

历史记录往返缓存service worker相关缓存冻结标签页 等不在HTTP缓存的范畴之内,这些部分作为浏览器产品本身的特色功能,没有标准可循。

第二部分:开发者实践

没睡着吧? 🫠

彻底阻止浏览器缓存任何资源

根据 no-store 的描述:
缓存绝对不可以存储请求或响应得任何部分,也绝对不可以使用改响应来满足请求
给所有的响应都加上 Cache-Control: no-store 的响应头就完事了。
😉
就是这么简单

渐进式添加缓存

一般来讲,没必要用no-store彻底阻止缓存,可以将 no-store 改进为 no-cache 。表示 该条缓存在验证成功之前绝对不可以用来响应请求。 浏览器在遇到这样的响应后通常会将其加入缓存,但是后续每次命中后都会进行验证,这比较符合开发者的预期。
到这一步,开发者理论上就不会再因为缓存的事情而烦恼了。同时也能保证缓存验证占用较小(单条缓存的验证通常只占用不到1KB的带宽,具体请参考缓存验证 )的公共带宽。

接下来讨论更细化,更具体场景的缓存控制。
💡
更细粒度的调整对带宽的影响已经微乎其微,缓存验证 已经接近0带宽占用。
但是高丢包、易断网的场景,no-cache 在成功验证之前无法用作响应,其造成的负面影响就会比较大。
另一方面,当引入缓存中介后也是要对缓存控制整体进行重新考虑。

针对永远不会变化的资源

💡
例如 字体图片等文件,以及经过构建的常规资源文件(先进的构建过程会为文件名添加哈希,从缓存视角,会视不同 URI 为不同资源)
  1. 允许被中间件缓存(减小源服务器的带宽压力)
  1. 在用户端强缓存(这类文件通常不会出现同名不同内容的情况)
Cache-Control: public,max-age=31536000,immutable
31536000秒=365天=1年 这条资源将缓存1年(永久性缓存)
immutable 缓存新鲜期间,无需重新验证; 这个指令属于扩展标准,在现代浏览器实现中,更多承担语义化的作用。
 
对客户端的意义:成功下载之后,非文档资源即使断网后也经得起刷新。
 

针对需要快速响应变化的资源

💡
例如传统前端项目的 html cssjs等文件,以及构建后的html文件。
这些文件可以缓存,但是每次访问前必须重新验证一下(比如出现临时性改动,希望不经过构建快速上线,e.g 添加 vconsole, 直接在构建产物 index.html 中添加 CDN script
Cache-Control: max-age=0,must-revalidate 或者 no-cache

 
⚠️
除了IANA公开的标准的缓存指令外,各家还可以自定义响应头来实现缓存, 比如常见的 X-Cache xxx 等等,各 CDN 厂商通常会有各自的实现。

备忘单

缓存简单示意。图片来自于网络,印象里是极客公园,确实不太记得了,侵删。
缓存简单示意。图片来自于网络,印象里是极客公园,确实不太记得了,侵删。

鉴定是否存在缓存问题

1 看请求方法

首先,GET请求通常要考虑缓存。
进一步细分,GET请求包含了 静态资源加载 和 API 两部分。
💡
你知道吗?
也就是说,API 也可以合理使用缓存机制,减少对服务器的请求次数。 服务端设计加缓存头和上redis的区别?一个缓存在终端,一个缓存在本地。

2 看响应头

然后是响应头,按优先级从高到低,需要在意:
  1. Cache-Control
  1. Expires(max-age存在时,会覆盖该字段语义 #section-5.3-8)
  1. Pragma(废弃)
 

高频问题回复

问题1: 浏览器缓存和 WebView 内的缓存 区别?
: 缓存组件存在于浏览器内核中,而非商业浏览器这个外壳中。内核暴露了一定的配置项,供浏览器配置。
WebView 是基于浏览器内核定制(简化)的用于嵌入程序使用的浏览器组件,其也提供一定的配置项。 在不做特殊配置的情况下,两者体验一般是一致的。
💡
你知道吗?
同内核的webview和内核通常是同一个团队的产品,先有内核更新,随后会更新对应的webview产品。
 
问题2:现代构建工具在减轻开发者对缓存认知方面的负担上做了哪些努力?开发服务器对缓存的默认配置?
  1. 构建后会为文件名附加哈希后缀 原理:(#section-4) 文件名作为URI的一部分,变更后,旧的缓存就无法匹配新请求了。
  1. 缓存响应头
vite源码,请求的资源是依赖的话,则设定缓存策略为不可变; 非依赖则使用 no-cache(必须经过验证)
vite源码,请求的资源是依赖的话,则设定缓存策略为不可变; 非依赖则使用 no-cache(必须经过验证)
实际项目运行,符合预期
实际项目运行,符合预期
 
<ins/>

课后练习

📎
场景:有一个老前端项目(不含构建脚本),没有任何缓存处理措施,用户直接访问 web 服务器,没有中介,实际使用环境为手机钉钉webview内。现在针对项目中的一个html文件,html中以script形式引用了一个js文件。由于业务需求频繁,这个js文件每周都要改。这会儿只修改了这个js文件,此时将js文件上传到 web服务器 中将原文件进行覆盖。
 
问题1:不考虑用户有清空缓存等操作,经常访问这个页面(每天访问)的老用户下次进入页面后会访问到新的js文件吗? A: 不会
B: 会
C: 可能会
答案
C: 可能会;
每周都要改,两次改动的时间间隔可以认为是在10天以内。根据启发式缓存规则,新鲜生命周期 = 10天*0.1 = 1天。 也就是说,不管是谁,在服务器资源更新之后,最多只会在1天内访问到旧版本的js资源
 
问题2:用户进入页面后如果发现页面还是旧版本的,他刷新了页面,刷新后能访问到新版本的资源吗?为什么?
答案
不能,因为刷新只会重新验证主资源,js作为子资源在刷新的过程中始终遵循原来设定的缓存规则,此时参照问题1的答案。
 
问题3:开发者将html中引用jsscript<script src=”./biz.js”></script> 改为<script src=”./biz.js?v=1.1”></script> ,你能理解他这么做的原因吗?以及这么做有用吗?
答案
资源地址更新后,缓存眼中,新旧资源双方无法匹配,所以会重新请求资源。
有用吗? 有用,但不彻底。修改了html内容后,如果用户直接导航进入(在移动端webview内通常难以/无法刷新,只能导航),则作为主资源,html依然会遵循之前的缓存规则。 但是相比没加后缀的情况, 现在起码刷新一次后就会加载正确的资源了。
 
问题4:本次业务调整还涉及到另一个非常严重的bug,1分钟之后如果用户还访问的是旧版本的js,就会造成无法挽回的后果。请问身为开发者,你会怎么做?
答案(思考答案请不要被代入而在意1分钟的限制哦 😄)
当缓存遵循启发式规则的情况下,没有办法立刻让开发者迅速接管缓存。
一个思路是沿着用户实际操作路径看看有没有突破口,例如用户不是通过url直接进入, 而是通过钉钉工作台里面一个图标进入的。不难发现 钉钉工作台在链接更新之后的几秒内会立刻刷新。此时先进入钉钉后台,将地址添加上后缀以去掉缓存。然后再在html文件中将该js文件引用也加上后缀(当然这步其实要先做)。 此后用户点击钉钉工作台图标后,导航结果就不会再命中缓存,对应的js资源也不会。
如果用户直接通过URL进入。那最坏的情况,或许即使你关掉服务器,用户依然能正常访问页面。如果关键业务在前端,此时或许只能听天由命了。(博主受限于认知,如果其实有办法逆转,欢迎私信打我脸hh)

参考

 

缓存验证

涉及到的请求/响应头有
Age Vary
Last-ModifiedIf-Modified-SinceIf-Unmodified-SinceLast-Modified-By
ETagIf-None-MatchIf-MatchIf-Range
 
 

本文待办

webview 部分有待进一步探究。
 
  • 前端
  • nginx
  • 缓存
  • HTTP
  • 浏览器
  • 黑群辉从无到有完善指南[存档,作者已转白]从开发人员的视角面对c盘容量紧缺的一些方案
    Loading...