浏览器缓存机制
浏览器的缓存机制是提升性能的重要手段,通过缓存资源减少重复的网络请求,降低带宽消耗,加快页面加载速度。
浏览器在请求资源时,通常会依赖缓存来决定是否需要重新从服务器获取资源。
相关HTTP标头
在讲解缓存的具体机制之前,先了解一下与缓存相关的HTTP标头。此处的字段和具体字段内容并不全面,只是列举了一些与本篇文章相关的内容。
Expires
HTTP/1.0 引入的字段,用来指定资源的过期时间。它使用的是绝对时间,表示资源在这个时间之前有效。
httpExpires: Wed, 21 Oct 2023 07:28:00 GMT
NOTE
但是,
Expires
字段存在一个问题,就是服务器返回的时间是服务器的时间,这在客户端和服务器时间不同步的情况下可能会出现问题。
Cache-Control
(与Expires
同时存在时,该优先级更高)HTTP/1.1 引入的字段,主要用来定义资源在客户端或代理服务器(如 CDN)中的缓存策略。它可以指定缓存的存储策略、过期时间、私有或共享等。
httpmax-age 表示该资源可以在客户端缓存 3600 秒(1 小时) 在这段时间内不会向服务器重新请求。 Cache-Control: max-age=3600 private 表示只能被私有缓存(如浏览器)缓存,不能被共享缓存。 适用于个性化或用户相关的数据。 Cache-Control: private public 表示可以被共享缓存(如 CDN)缓存。 Cache-Control: public no-store 表示不缓存该资源。 Cache-Control: no-store no-cache 表示可以使用缓存,但必须先向服务器发起请求验证资源是否有效(通过 ETag 或 Last-Modified 头验证)。 Cache-Control: no-cache
Last-Modified
和If-Modified-Since
Last-Modified
是服务器响应头,表示资源的最后修改时间。 当资源超过max-age
时间后,浏览器会在下一次请求该资源时,通过 If-Modified-Since 头字段将这个时间发送给服务器,询问资源是否自那时以来有过修改。如果没有修改,服务器返回
304 Not Modified
,浏览器可以继续使用本地缓存。同时更新资源的过期时间。httpHTTP/1.1 200 OK Content-Type: text/html Content-Length: 1024 Date: Tue, 22 Feb 2022 22:22:22 GMT Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT Cache-Control: max-age=3600 <!doctype html> …
httpIf-Modified-Since: Tue, 22 Feb 2022 22:00:00 GMT
NOTE
Last-Modified
的精度是秒级,因此对于频繁修改的资源可能不够精确。如果资源是动态生成的(即使内容没有变化,每次生成时间也会不同),
Last-Modified
可能不适用。
ETag
ETag
(Entity Tag)是服务器为每个资源生成的唯一标识符,用于标识资源的版本或内容。 与Last-Modified
不同,ETag
可以是任何形式的标识符(例如hash值),不依赖时间戳,精度更高。浏览器在请求资源时,会在请求头中携带
If-None-Match
字段,该字段的值就是之前资源的ETag
。服务器通过比较
ETag
来判断资源是否发生变化。如果没有变化,返回304 Not Modified
;如果变化了,则返回新的资源。httpHTTP/1.1 200 OK Content-Type: text/html Content-Length: 1024 Date: Tue, 22 Feb 2022 22:22:22 GMT ETag: "deadbeef" Cache-Control: max-age=3600 <!doctype html> …
httpGET /index.html HTTP/1.1 Host: example.com Accept: text/html If-None-Match: "deadbeef"
缓存的种类
浏览器缓存主要分为两种:私有缓存 和 共享缓存。
私有缓存
私有缓存由Cache-Control: private
控制,仅作用于当前用户的浏览器,不会被共享缓存(如 CDN)缓存。
私有缓存相当于服务器基于一定规则向浏览器发放的一份独立资源,只有当前用户可以使用,其他用户无法使用。 因为每个用户的缓存是独立的,私有缓存适合用来缓存个性化的内容或与用户相关的敏感信息,确保不会因为缓存共享而导致数据泄露。
共享缓存
共享缓存(Shared Cache)是指多个用户或多个客户端可以共同使用的缓存,通常用于减少重复的网络请求,提高性能,减轻服务器负担。
它存在于中间代理、内容分发网络(CDN)、企业网关或其他公共缓存服务器上。
目的是将资源缓存到靠近多个客户端的地方,使得这些客户端可以共享同一份资源副本,而不必每次都从原始服务器获取资源。
根据具体的应用场景不同可以分为以下两种共享缓存
代理缓存:通常位于企业、组织或网络服务提供商的代理服务器上。 适合内部有大量用户访问相同资源的情况。 减少了对外部网络的依赖,降低了带宽使用,提高了资源获取的速度,尤其是在慢速网络环境下。
托管缓存:通常是由专门的托管服务(例如 CDN)提供的缓存服务,是一种专门用于加速内容传递的网络。 适用于大规模、全球性用户访问的应用场景。 通过将内容缓存到靠近用户的位置,减少网络延迟,提高访问速度。
启发式缓存
在正式讲解浏览器缓存机制的流程之前,先了解一下启发式缓存(Heuristic Caching)。
启发式缓存是用于处理那些没有明确提供缓存控制头(如 Cache-Control
或 Expires
)的资源。 这种推测机制虽然不如明确的缓存指令精确,但可以在某些情况下提高性能,避免频繁向服务器请求相同的资源。
常见的启发式方法是使用 Last-Modified
到请求时间的间隔作为参考,通常浏览器会缓存该资源一段时间(例如,这个间隔时间的 10% 或其他固定比例)。
如果该资源 10 天前修改过,而没有明确的缓存控制字段,浏览器可能会缓存该资源 1 天(10 天的 10%)。
启发式缓存是在 Cache-Control
被广泛采用之前出现的一种解决方法,基本上所有响应都应明确指定 Cache-Control
标头。
浏览器缓存机制
浏览器的缓存判定通常有两个过程:强缓存 和 协商缓存。
强缓存不需要向服务器发送请求,首先检查本地缓存中是否有该资源的缓存副本,并且检查缓存是否未过期。
这种机制通过 HTTP 响应头中的 Cache-Control
(具体为其中的 max-age
值) 或 Expires
来实现。
如果强缓存失效,将进入协商缓存的流程。
浏览器会通过 If-Modified-Since
或 If-None-Match
请求头带上 Last-Modified
或 ETag
的值,请求服务器验证缓存是否仍然有效。
如果服务器响应 304 Not Modified
,浏览器会使用本地缓存资源,同时更新强缓存的有效时间。
如果服务器响应 200 OK
,则返回新的资源,并更新缓存。