浏览器缓存策略

545 阅读7分钟

1. 前言

缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。这样带来的好处有:缓解服务器端压力,提升性能(获取资源的耗时更短了)。对于网站来说,缓存是达到高性能的重要组成部分。缓存需要合理配置,因为并不是所有资源都是永久不变的:重要的是对一个资源的缓存应截止到其下一次发生改变(即不能缓存过期的资源)。

缓存的种类有很多,其大致可归为两类:私有与共享缓存。共享缓存存储的响应能够被多个用户使用。私有缓存只能用于单独用户。本文将主要介绍浏览器与代理缓存,除此之外还有网关缓存、CDN、反向代理缓存和负载均衡器等部署在服务器上的缓存方式,为站点和 web 应用提供更好的稳定性、性能和扩展性。--- 摘自《MDN - HTTP 缓存》

本文主要介绍浏览器缓存(即私有缓存)。在大多数场景下,缓存是最常见的优化手段,简单高效~

缓存的优势特点:

  • Client: 重复利用资源,减少网络开销,请求更快地响应解析,更好的用户体验
  • Network: 减少重复资源的网络开销,节省带宽,降低成本
  • Server: 减小服务器负载,避免过载造成性能瓶颈,简单高效的性能优化手段

2. 缓存机制

常见的 HTTP 缓存只能存储 GET 响应,对于其他类型的响应则无能为力。缓存的关键主要包括request method和目标URI(一般只有GET请求才会被缓存),流程图如下:

2.1 强缓存

强缓存:如果命中强缓存,浏览器发送的请求不会到达服务器,而是直接从浏览器缓存中读取数据,在chrome network中size显示为 disk cache 或者 memory cache,HTTP 状态码为200。

强缓存的字段:Expires(HTTP/1.0)、Cache-Control(HTTP/1.0)、Pragma (HTTP/1.0,效果同Cache-Control: no-cache,主要为了兼容,不做赘述)

2.1.1 Expires

是一个绝对的时间,指定资源到期时间是服务器的具体时间点,该字段存在于服务器响应头, 即在此时间之前,直接读取浏览器缓存数据。如果值为 0, 代表着过去的日期,即该资源已经过期。

Expires的优点:

  • HTTP/1.0 的产物,向后兼容

  • 绝对时间标示失效时间,方便对比

Expires的缺点:

  • 由于是绝对时间,客户端可以通过修改本地时间的方式,使缓存失效
  • 时间是由服务端发送的,可能存在服务器时间与客户端时间不一致
  • 在缓存未过期之前,客户端是无法得知资源是否修改

2.1.2 Cache-Control

已知Expires的缺点之后,在HTTP/1.1中,新增了一个字段Cache-control,该字段表示资源缓存的最大有效时间(相对时间),在该时间内,客户端不需要向服务器发送请求。

Cache-control常用值如下:

常用值字段说明
private默认值,所有内容只有客户端才可以缓存,代理服务器不能缓存
public所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)
max-age表示资源能够被缓存(保持新鲜)的最大时间,单位为s
must-revalidate如果超过了max-age的时间,浏览器必须向服务端发送请求,验证资源是否还有效
no-cache不使用强缓存,浏览器在使用缓存数据时,需要与服务器验证缓存是否新鲜
no-store真正意义上的“不要缓存”,所有内容都不走缓存,包括强制和对比
s-maxage设置共享缓存,比如CDN。会覆盖max-age和Expires
max-stale能容忍的最大过期时间,表示客户端愿意接收一个已经过期的资源
min-fresh能够容忍的最小新鲜度。标示了客户端不愿意接受新鲜度不多于当前的age加上min-fresh设定的时间之和的响应

Cache-Control的优点:

  • HTTP/1.1 的产物,解决Expires服务器与客户端相对时间不一致的问题
  • 可以组合使用指令,达到多个目的

Cache-Control的缺点:

  • 在HTTP/1.0中,无法使用

  • 在缓存未过期之前,客户端是无法得知资源是否修改

2.2 协商缓存

当强缓存失效or没设置时,由客户端与服务器通过某种标识来判断(协商)资源是否可用,所以字段是成对出现的。请求头If-Modified-Since和响应头Last-Modified配对,请求头If-None-Match和响应头ETag配对。如果命中,HTTP 状态码为304。

2.2.1 Last-Modified和If-Modified-Since

是一个绝对的时间 ,表示服务器的资源最后一次修改的时间,在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段,对比该次请求响应头Last-ModifiedIf-Modified-Since 时间,若时间一致,返回304状态码。

Last-ModifiedIf-Modified-Since优点:

  • HTTP/1.0 的产物,向后兼容

  • 由于每次都经过服务器检验,不存在客户端无法得知资源是否修改的问题

Last-ModifiedIf-Modified-Since缺点:

  • 如果服务器资源更新在1S以内,可能无法识别1S内多次修改的情况
  • 如果请求的是动态资源,无法起到缓存作用。例如:SSR返回的页面

2.2.2 Etag和If-None-Match

为了解决Last-ModifiedIf-Modified-Since的缺点 ,在HTTP/1.1 新增了EtagIf-None-Match字段,存储的是文件的特殊标识(一般由Hash生成),例如 Nuxt.js 服务端渲染页面时自带 Etag。 同样在请求头中将上次的 Etag 的值写入到请求头的 If-None-Match 字段,对比该次请求响应头EtagIf-None-Match 值,若一致,返回304状态码。

EtagIf-None-Match优点:

  • 在HTTP/1.0中,无法使用

  • 由于每次都经过服务器检验,不存在客户端无法得知资源是否修改的问题

  • 可以更加精确的表示资源内容是否修改,可以识别1S内多次修改内容的情况

EtagIf-None-Match缺点:

  • 计算Etag值会带来额外的性能损耗
  • 如果集群中服务器算法不一致,会导致Etag对比失效,无法起到协商缓存的作用

3. 用户行为对浏览器缓存的影响

用户操作Expires/Cache-ControlLast-Modied/Etag
地址栏回车
页面链接跳转
新开窗口
前进、回退
F5刷新X
Ctrl+F5强制刷新XX

5. Vary

Vary HTTP 响应头决定了对于后续的请求头,如何判断是请求一个新的资源还是使用缓存的文件。当缓存服务器收到一个请求,只有当前的请求和原始(缓存)的请求头跟缓存的响应头里的Vary都匹配,才能使用缓存的响应。

简而言之,可以通过Vary字段来通知缓存哪些请求头字段用于区分相同的URL请求,服务端存在不同内容的响应。

6. CDN(公有缓存)

在大多数前端项目中,静态资源都会使用CDN加速用户访问。CDN缓存策略因为服务商的不同而不同,但一般会根据源站响应头中的Cache-ControlExpires 字段来设置节点缓存时间。因此,CDN缓存返回的Cache-ControlExpires 一般源自服务器或者Nginx配置。

7. 总结

  • 优先级:强缓存高于协商缓存,Cache-Control高于ExpiresEtag高于Last-modified
  • 可以通过状态码来区分其为两大类,协商缓存为304和强缓存为200
  • 如果服务器输出了 ETag,没有必要再输出 Last-ModifiedEtag更能精准标识资源的修改情况
  • Etag不是自带的,有些框架会默认添加,例如 nuxt.jsnodejs可以使用 weak tag

8. 参考