HTTP Web 缓存总结

HTTP Web 缓存总结
Photo by Sam 🐷 / Unsplash

友情提示:缓存什么的,是完全依赖相关http header头信息来标记和判断的

缓存读取顺序:

首先读取本地缓存,如果条件满足就取本地缓存,否则往后走代理缓存,同理,条件满足就是从代理缓存取资源(可能存在多级代理缓存)

如果一条链路上的资源都不符合,那么就去源服务器获取

缓存优先级:Cache-Control > Expires > Etag > Last-Modified

缓存的分类和优先级

  • 强缓存 状态码 200 (比如 200 (from cache))
    • Expires 服务器下发的绝对时间,而判断的时候以浏览器时间为准,客户端和服务器有可能会不一样
    • Cache-Control 相对时间,以客户端相对时间为准
  • 协商缓存 状态码 304
    • Last-Modified If-Modified-Since
    • Etag If-None-Match

强缓存优先级高于协商缓存,强缓存不会询问服务器,直接使用缓存。协商缓存会询问服务器关于文件的可用性

对于传输过程中的中间节点,本文都称为代理服务器,包括proxy、CDN和缓存服务器

缓存头Cache-Control

可缓存性 (哪些节点可以进行缓存)

  • public 在这个请求的返回过程中的任何节点都可以对这个返回的内容进行缓存,比如代理服务器可以对其缓存
  • private 只有发起请求的节点才能进行缓存,其他节点都禁止缓存资源
  • no-cache 可以进行缓存,但是在使用之前,要去服务器先验证缓存的有效性,有效的话才能使用,搭配max-age=0使用,不会直接从浏览器读取相应的缓存文件,而是先向服务端发起请求确认浏览器的缓存是否过期来判断是从浏览器加载还是从服务器加载
  • no-store 任何节点都不能进行缓存

到期时间

  • max-age = 多少秒之后缓存内容过期
  • s-maxage = 只在代理服务器生效。优先级大于max-age。如果s-maxage时间到了,代理服务器会去远端读取新的数据并更新缓存
  • max-stale = 即便缓存过期了,只要在max-stale的范围,则不需要请求新的缓存

重新验证

  • must-revalidate 在设置了max-age过期之后,浏览器必须去源服务端获取新的数据
  • proxy-revalidate 代理服务器数据过期后,必须去源服务端获取新的数据

其他

  • no-transform 用在代理服务器,不允许对返回内容进行压缩、格式转换等

上述头信息是规范,但是很多代理服务器不按这个来

缺点:Cache-Control为客户端缓存,服务端更新了文件之后,客户端并不第一时间更新缓存,这也是web开发中的常见问题

Cache-Control缺点的通用解决方案:在打包文件之后,根据静态文件内容加上一串hash码或者版本号,如果静态文件内容没变,则hash不会变,反之,静态文件内容变化之后,hash码变化,则静态资源url发生变化,可以更新客户端缓存

举例

我司的处理:可以看到,devTools的Size栏,提示出from memory cache,即读取浏览器本地缓存

image.png
示例代码:这里是用模板来加载的,support来填充,猜测可以在初始化请求的时候,通过请求配置接口,拿到对应的web资源的版本号的接口或者配置文件,然后再静态资源去加载之前,将对应的版本号填充到对应的src或者link中

资源验证头信息Last-Modified和Etag

Last-Modified

资源上次修改时间,主要配合If-Modified-Since或者If-Unmodified-Since使用:如果请求了一个资源的respose header里面有Last-Modified头信息指定了一个时间,下一次浏览器发起请求的时候,通过If-Modified-Since或者If-Unmodified-Since(通常使用If-Modified-Since,If-Unmodified-Since很少用)带上那个时间,带到服务器,服务器读取并对比这个资源上次修改的时间,如果一致,则代表资源没有被修改过,那么浏览器可以继续使用缓存,反之缓存不能使用

ps:如果资源是存在数据库的,可以给资源加上一个update-at的字段加上修改时间作为Last-Modified时间返回给浏览器

Etag

更加严格的验证方式,通过数据签名的方式,根据资源的内容产生一个唯一的签名,如果资源的内容产生了修改,那么签名就会更新,任何的修改都会导致签名不一致,常用方式是对资源的内容进行hash计算产生签名来标记资源。

配合If-Match或者If-Non-Match使用,下一次浏览器发请求会带上If-Match或者If-Non-Match头,头里面的值就是服务端上一次给出的Etag签名,然后服务器对比签名是否一致

http code 304资源没有修改,可以直接读取缓存,及时这时候服务端有了新的返回,也会被浏览器忽略,直接读取缓存数据(写个接口,而不是静态资源文件来验证一下)

tips:

chrome的devTools,当把Disable cache勾选上之后,浏览器就不往服务器发送关于缓存的头信息了,这时候只能从服务器读取最新的数据

缓存对比localStorage: 缓存文件不用自己去控制localStorage的读写呢
image.png

本文是 18年我发布在 Segmentfault 社区的,时间过得真快

Read more

从事 SEO 12 年的大佬总结的要点

从事 SEO 12 年的大佬总结的要点

从事 SEO 12 年后,以下是我能想到的所有 SEO 技巧: 1. 在 H1、H2 和 URL 部分中使用主要关键词。 2. 停止追逐虚荣指标。流量固然重要,但转化率才是最重要的。 3. 内部链接可以将“几乎完成”的页面转变为表现最佳的页面。 4. 定期检查你的网站是否有坏链接,这是一个简单的 SEO 胜利。 5. Google Search Console 是您最好的朋友,使用它来重新优化排名在第 3-7 位的页面。 6. 90% 的反向链接应来自相关的高权威网站。 7. 速度决定一切——或许应该说,速度不足会扼杀一切。确保加载时间在 2 秒以内。 8. 持续更新内容将帮助你的页面保持更高的排名。 9. 使用 NLP

Pnpm 构建 Monorepo

Pnpm 构建 Monorepo

最近开始搭建 monorepo 的项目,了解到以下方案: * lerna 有强大完善的依赖管理和构建、打包发布流程,而且 v5 版本将 nx 与 lerna 整合了,提升了性能和速度 * 业界大多使用 lerna + yarn workspace 的方式来管理,yarn 用来管理依赖,lerna 用于管理发布 之前简单使用过 lerna ,不过再加上 yarn workspace , 学习容多、配置多又得折腾,太费劲🤪,暂时搁置。 最终尝试使用单一的 pnpm workspace + changesets 的方式来构建 monorepo,本身我就使用 pnpm 作为包管理工具,目前我的场景简单,pnpm 就已足够。 本文记录自己使用 pnpm workspace 搭建 monorepo

关于 Webpack 使用 Babel 与 Vite 使用 ESBuild 的一些思考

关于 Webpack 使用 Babel 与 Vite 使用 ESBuild 的一些思考

虽然 Vite 是基于 esbuild 构建的,并且 esbuild 在构建速度上有显著优势,但 Webpack 目前并没有直接使用 esbuild 构建的原因涉及多个方面,主要是 架构设计差异、功能需求 和 兼容性 等问题。下面是一些关键的原因: 1. Webpack 的复杂性与扩展性 • Webpack 的高度可扩展性:Webpack 的设计目标是一个 高度可配置和可扩展的打包工具。它提供了丰富的插件和 loader 机制,支持各种复杂的构建需求(如代码分割、热更新、各种资源处理等)。这些功能要求 Webpack 必须具备更细粒度的控制能力,而 esbuild 的设计理念相对简化,主要关注高速构建和基本的模块转换功能。 • Loader 和 Plugin 系统:Webpack 的强大之处在于其 loader 和

Dokploy 一键部署 Plausible

Dokploy 一键部署 Plausible

Plausible 是一款与 Google Analytics 类似的 Web 流量分析工具,相比 Google Analytics 对比如下: * 功能复杂度:GA 功能更强大,适合需要深入分析的用户;Plausible 提供基础的流量分析,简单易用。 * 自托管:Plausible 可以自托管,GA 不支持;GA 存储数据在 Google 服务器上。 * 性能:Plausible 更轻量,只有几 KB,加载更快;GA 可能影响网站性能。 * 隐私保护:Plausible 更注重隐私,不追踪用户数据;GA 会使用 cookies 并可能共享数据给 Google。 我是用 Plausible 来监控我的个人的一些站点,这里使用 Dokploy 来一键部署