HTTP 缓存
HTTP 缓存主要分为两大类:强缓存和协商缓存。这两种缓存都通过 HTTP 响应头来控制,目的是提高网站性能和降低服务器负载。
1. 强缓存:
解释
强缓存不需要向服务器发送请求,直接从客户端(如浏览器)缓存中读取资源。强缓存主要通过以下两种响应头实现:
- Expires:该字段指定一个具体的过期时间,例如"Expires: Wed, 21 Oct 2023 07:28:00 GMT"。当客户端再次请求资源时,如果当前时间未超过 Expires 指定的过期时间,则直接从缓存中获取资源。
- Cache-Control:该字段具有更多的选项,比如"Cache-Control: max-age=3600",表示资源在 3600 秒(1 小时)内有效。如果客户端的缓存资源未过期,就直接使用缓存资源,无需向服务器请求。
举例
- Expires 假设一个图片资源在一周内不会发生变化,我们希望客户端在这段时间内直接使用缓存中的图片,而不是每次访问页面时都向服务器发送请求。为了实现这一目标,我们可以在服务器端设置 Expires 响应头,例如:
yaml
Expires: Tue, 25 Apr 2023 08:00:00 GMT
此示例表示图片资源在指定的过期时间(2023 年 4 月 25 日 08:00:00 GMT)之前都有效。在这期间,客户端无需向服务器发送请求,直接从缓存中获取该图片资源。 需要注意的是,Expires 的过期时间是一个绝对时间点,而不是一个时间段。此外,由于 Expires 是基于客户端时间的,因此可能会因客户端和服务器时间不同步而出现问题。会拿Expires标注的时间与客户端的系统时间进行比较。为避免这类问题,现代 Web 开发中更推荐使用 Cache-Control:max-age 来设置相对时间的缓存策略。
- Cache-Control
- public:资源可以被任何客户端和代理服务器缓存。例如:
yaml
Cache-Control: public, max-age=3600
此示例表示资源在接下来的 3600 秒(1 小时)内有效,可以被客户端和代理服务器缓存。
- private:资源仅可被特定客户端缓存,不能被代理服务器缓存。例如:
yaml
Cache-Control: private, max-age=3600
此示例表示资源在接下来的 3600 秒(1 小时)内有效,但只能被客户端缓存,代理服务器不能缓存
- no-cache:资源不能被缓存,客户端需要向服务器发起请求验证资源是否有更新。例如:
yaml
Cache-Control: no-cache
此示例表示客户端每次请求资源时都需要向服务器验证资源是否有更新,即使资源已经被缓存。
- no-store:资源不允许被缓存,客户端每次都需要向服务器请求完整资源。例如:
yaml
Cache-Control: no-store
此示例表示资源不能被客户端缓存,每次访问都需要重新从服务器请求资源。
- max-age:资源在指定时间(以秒为单位)内有效。例如:
yaml
Cache-Control: max-age=3600
此示例表示资源在接下来的 3600 秒(1 小时)内有效。客户端在此期间可以直接从缓存中获取资源,无需向服务器发送请求。
- s-maxage:资源在指定时间(以秒为单位)内对代理服务器有效。例如:
yaml
Cache-Control: s-maxage=7200
此示例表示资源在接下来的 7200 秒(2 小时)内对代理服务器有效。代理服务器在此期间可以直接从缓存中获取资源,无需向源服务器发送请求。注意,此指令仅对代理服务器有效,客户端会忽略它。
- must-revalidate:一旦资源过期,客户端必须向服务器发起请求以验证资源是否有更新。例如:
yaml
Cache-Control: max-age=3600, must-revalidate
此示例表示资源在 3600 秒(1 小时)内有效。一旦过期,客户端需要向服务器验证资源是否有更新,而不能直接使用过期的缓存。
- proxy-revalidate:与 must-revalidate 类似,但仅适用于代理服务器。例如:
yaml
Cache-Control: s-maxage=7200, proxy-revalidate
此示例表示资源在 7200 秒(2 小时)内对代理服务器有效。一旦过期,代理服务器需要向源服务器验证资源是否有更新,而不能直接使用过期的缓存。注意,此指令仅对代理服务器有效,客户端会忽略它。
- no-transform:禁止代理服务器对资源进行转换或修改。例如:
yaml
Cache-Control: no-transform
此示例表示代理服务器在缓存和传输资源时不得对其进行转换或修改,例如更改图片格式或压缩文件。
- immutable:资源内容不会更改,客户端和代理服务器可以无限期地缓存。例如:
yaml
Cache-Control: public, max-age=31536000, immutable
此示例表示资源可以被客户端和代理服务器缓存,其内容永远不会更改。max-age 设置为 31536000 秒(1 年),但由于 immutable 指令,客户端和代理服务器可以继续使用缓存,即使超过 max-age 指定的时间。
通过以上示例,可以了解到 Cache-Control 指令的多样性以及如何根据不同场景设置合适的缓存策略。实际应用中,可以根据需要组合使用这些指令,实现更精细化的缓存控制。
2. 协商缓存:
解释
协商缓存需要客户端向服务器发送请求,询问资源是否有更新。如果资源没有更新,服务器返回 304 状态码,客户端继续使用本地缓存;如果资源有更新,服务器返回 200 状态码,并返回新的资源。协商缓存主要通过以下两对响应头和请求头实现:
- Last-Modified 和 If-Modified-Since:服务器通过 Last-Modified 响应头告知客户端资源的最后修改时间。客户端在后续请求中通过 If-Modified-Since 请求头携带该时间,服务器判断资源是否有更新。如果没有更新,返回 304 状态码。
- ETag 和 If-None-Match:服务器通过 ETag 响应头给资源生成一个唯一标识符。客户端在后续请求中通过 If-None-Match 请求头携带该标识符,服务器根据标识符判断资源是否有更新。如果没有更新,返回 304 状态码。
举例
-
Last-Modified 和 If-Modified-Since 假设一个新闻网站的首页,其内容会不定时更新。为了避免频繁向服务器请求新内容,我们可以使用协商缓存。首先,在服务器端设置 Last-Modified 或 ETag 响应头,例如:
yamlLast-Modified: Wed, 14 Apr 2023 10:20:30 GMT
当客户端首次请求该资源时,会收到 Last-Modified 响应头,然后将此值存储在本地。下次请求时,客户端会携带 If-Modified-Since 请求头,例如:
yamlIf-Modified-Since: Wed, 14 Apr 2023 10:20:30 GMT
服务器会比较资源的最后修改时间与 If-Modified-Since 的值。如果资源没有更新,服务器返回 304 状态码,客户端继续使用本地缓存;如果资源有更新,服务器返回 200 状态码,并返回新的资源
-
ETag 和 If-None-Match 假设一个博客网站的某篇文章内容会不定时更新。为了减少不必要的数据传输,我们可以使用 ETag 和 If-None-Match 实现协商缓存。首先,在服务器端设置 ETag 响应头,例如:
vbnetETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
当客户端首次请求该资源时,会收到 ETag 响应头,并将该值存储在本地。下次请求时,客户端会携带 If-None-Match 请求头,例如:
sqlIf-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
服务器会比较资源的当前 ETag 与 If-None-Match 的值。如果资源没有更新(ETag 值未变),服务器返回 304 状态码,客户端继续使用本地缓存;如果资源有更新(ETag 值发生变化),服务器返回 200 状态码,并返回新的资源。
问题
-
强缓存和协商缓存哪个优先级更高? 当一个请求发出时,浏览器首先会检查是否可以从强缓存(即本地缓存)中满足这个请求。 1、强缓存:如果强缓存有效(也就是缓存的资源还没有过期),浏览器会直接从强缓存中获取资源,而不会向服务器发送请求。在这种情况下,浏览器的开发者工具会显示状态码为 200,但是"size"会标记为"from cache"。 2、协商缓存:如果强缓存无效(比如说缓存的资源已经过期),那么浏览器就会启用协商缓存。它会向服务器发送一个请求,如果服务器返回 304 状态码(Not Modified),浏览器就会知道它可以使用缓存中的资源。如果服务器返回 200 状态码和新的资源,浏览器就会更新它的缓存并显示新的资源。 因此,从优先级的角度来看,强缓存优先级高于协商缓存。如果强缓存可以使用,浏览器就会直接使用,而不会尝试协商缓存。如果强缓存无效,浏览器才会尝试使用协商缓存。这种设计是为了最大限度地减少不必要的网络请求,提高网页加载的效率。
注意:
浏览器刷新页面(F5或者调用
location.reload()
)时,请求头中会携带rubyCache-Control:max-age=0
告诉浏览器不使用强制缓存而走协商缓存。
-
强缓存为 no-store 是不是也不会走协商缓存? 对的,no-store 是一个 Cache-Control 指令,当它被设置时,浏览器和所有中间缓存服务器都不应该存储任何版本的返回响应。这意味着每次用户请求该资源时,都会向服务器发送一个新的请求,并下载完整的响应。 所以,如果 Cache-Control 被设置为 no-store,那么强缓存和协商缓存都不会被使用。每次请求都会向服务器请求数据,这也意味着每次请求都会得到最新的数据,但是这也牺牲了缓存带来的性能优化。
总结
总结一下,强缓存和协商缓存的主要区别在于是否需要向服务器发送请求。强缓存直接从客户端缓存获取资源,而协商缓存需要请求服务器确认资源是否有更新。这两种缓存策略可以结合使用,实现更高效的资源加载。
注释
-
代理服务器 在 HTTP 中,代理服务器(Proxy Server)是一种位于客户端和目标服务器之间的服务器。代理服务器在许多场景中发挥作用,例如提供内容过滤、安全控制、负载均衡和缓存服务。当客户端发送请求时,代理服务器可以代替客户端与目标服务器进行通信,然后将响应返回给客户端。在 Cache-Control 中提到的代理服务器主要指的是缓存代理服务器(Caching Proxy Server)。缓存代理服务器可以缓存从目标服务器获取的资源,从而在后续请求中减少对目标服务器的负载和提高内容分发速度。当客户端向代理服务器发送请求时,代理服务器可以先检查本地缓存。如果缓存中有请求的资源且未过期,代理服务器直接将缓存的资源返回给客户端;如果缓存中没有资源或资源已过期,代理服务器会将请求转发给目标服务器,然后将新资源缓存并返回给客户端。Cache-Control 中的一些指令(如 s-maxage 和 proxy-revalidate)专门针对代理服务器设计。例如,s-maxage 指定资源在代理服务器的缓存有效期,而 proxy-revalidate 表示代理服务器在资源过期后需要向目标服务器验证资源是否有更新。通过在 HTTP 响应中设置适当的 Cache-Control 指令,可以控制代理服务器如何缓存资源,进一步优化网站性能和降低目标服务器负载。
-
ETag 是怎么生成的 ETag(Entity Tag)是一种资源的唯一标识符,通常用于协商缓存。服务器会根据资源内容生成一个 ETag 值,并将其放在 HTTP 响应头中。生成 ETag 的方法有多种,包括但不限于以下几种:
-
文件内容的哈希值:通过计算文件内容的哈希值(如 MD5、SHA-1 等)来生成 ETag。这种方法的优势在于即使文件的元数据(如修改时间)发生变化,只要文件内容没有变化,ETag 就不会改变。
-
时间戳与文件大小的组合:将文件的最后修改时间(或其他时间戳)与文件大小进行组合,生成一个唯一标识符。例如,可以将最后修改时间的毫秒数与文件大小拼接在一起。这种方法简单且高效,但在某些情况下可能导致误判(例如,文件内容变化但大小未变)。
-
自定义算法:可以根据具体需求和场景开发自定义算法,生成资源的唯一标识符。这种方法灵活性较高,但可能需要更多的开发和维护工作。
无论采用哪种生成方法,ETag 的目标都是确保资源的唯一标识符能够反映资源内容的变化。当客户端发送带有 If-None-Match 请求头的请求时,服务器会比较请求头中的 ETag 值与当前资源的 ETag 值。如果两者相同,表示资源未发生变化,服务器返回 304 状态码;如果两者不同,表示资源已更新,服务器返回 200 状态码并返回新的资源。
-