浏览器缓存与304状态码

总结
出现304状态码的条件
- 不禁止缓存
- 不检查本地缓存是否过期;或者检查本地缓存、且未过期
- 响应头有
Etag
(则下次的请求头中有If-None-Match
);或者响应头有Last-Modified
(则下次的请求头中有If-Modified-since
) - 缓存有效
注意:下图中的Etag
和Last-Modified
之间不存在先后顺序
一、304请求的交互过程
假设用户通过浏览器访问某静态资源(比如图片image.png),且该资源之前已被浏览器缓存,现在分析两次请求的交互过程。
第一次请求(初始加载)
1、浏览器发送请求
用户在浏览器中输入 URL,浏览器向服务器发送 GET 请求,获取 image.png。
请求头可能如下:
1 | GET /image.png |
2、服务器响应
服务器处理请求,并返回 200 OK 响应,同时附带以下响应头:
1 | 200 OK |
Last-Modified
:表示资源最后修改的时间ETag
:资源的唯一标识符Cache-Control
:指示浏览器可以缓存该资源的时长
3、浏览器缓存资源
浏览器接收响应后,将image.png缓存到本地,并根据响应头信息(如Last-Modified
和ETag
)记录该资源的状态
第二次请求(缓存验证)
1、浏览器发送条件请求
当用户再次访问同一资源,浏览器会检查缓存,如果该资源仍然有效,就发送一个带条件的 GET 请求到服务器,请求头如下:
1 | GET /image.png |
If-Modified-Since
:告诉服务器,如果在指定日期之后资源被修改,则返回新的资源If-None-Match
:告诉服务器,如果资源的ETag
与提供的值不匹配,则返回新的资源
2、服务器处理请求
服务器接收到条件请求后,检查该资源的状态:
如果资源自Last-Modified
时间以来未被修改,且ETag
匹配,那么服务器不会返回新的内容,而是返回 304 Not Modified。
服务器返回如下响应:
1 | 304 Not Modified |
注意:304 响应没有消息体,通常只包含必要的状态码和一些响应头。
3、浏览器处理304响应
当浏览器接收到 304 Not Modified 响应时,了解到之前缓存的 image.png 仍然有效,因此直接使用缓存中的版本,而无需重新下载。
二、详细讨论
1、是否禁止缓存
禁止缓存:缓存中不得存储任何关于客户端请求和服务端响应的内容,客户端每次发起的请求都会下载完整的响应内容
在请求头中,Cache-Control: no-store
与 Pragma: no-cache
都可以禁止缓存
Pragma: no-cache
可用于http 1.0
和http 1.1
Cache-Control: no-store
只能用于http 1.1
2、是否检查本地副本是否过期
由 Cache-Control
的 no-cache
和 must-revalidate
两个可选值控制
no-cache
:告诉浏览器、缓存服务器,不管本地副本是否过期,使用资源副本前,必须到源服务器进行副本有效性校验must-revalidate
:告诉浏览器、缓存服务器,本地副本过期前,可使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验(如果没有该属性,若发生网络问题等情况,客户端可能直接使用本地缓存,而不会向服务器发起校验)
3、检查本地副本是否过期
与「缓存的过期机制」相关的响应头字段(优先级依次降低):
(1)max-age=<seconds>
表示资源能被缓存的最大时间,通常和 must-revalidate
一起使用。
本地副本过期前,可使用本地副本,缓存时间为60秒;
如果本地副本已过期,则重新向服务器请求(要求必须进行有效性校验):
1 | Cache-Control: max-age=60, must-revalidate |
共享缓存(如代理服务器)将在60秒后失效,而个人用户的缓存则在120秒后失效:
1 | Cache-Control: max-age=120, smax-age=60 |
(2)Expires
表示资源的过期时间。
如果无max-age
但有Expires
,通过比较Expires
和Date
的值来判断是否缓存有效:
(3)Last-Modified
表示资源的最后修改时间。
如果无max-age
和expires
但有Last-Modified
,缓存的时长等于
1 | (Date - Last-Modified) / 10 |
如果都没有,浏览器将不会对该需求进行缓存。
4、本地副本没过期
直接从缓存中读取资源,并返回200状态码。
5、本地副本已过期
进行「源服务器进行有效性校验」的前期准备:
(1)If-None-Match
首先,在请求头里寻找If-None-Match
字段(其值为服务器上次返回的ETag
响应头的值)
(2)If-Modified-Since
如果没有If-None-Match
,则在请求头中寻找If-Modified-Since
字段(其值为服务器上次返回的Last-Modified
响应头中的日期值)
如果If-None-Match
与If-Modified-Since
都没有,则直接向服务器请求数据
6、去源服务器进行有效性校验
如果请求头中有If-None-Match
或If-Modified-Since
,则到源服务器进行有效性校验。
如果源服务器资源没有变化,则返回304;如果有变化,则返回200。
7、上述流程的总结图
三、意义
- 避免了不必要的数据传输,提高了网络效率和用户体验;
- 在频繁访问相同资源的情况下,可以显著降低带宽消耗和服务器负载。
四、补充
私有缓存和公共缓存
在Cache-Control
还有两个值:private
与public
:
- public 表示该响应可被任何中间人(比如中间代理、CDN等)缓存。
- private 表示该响应是专用于某单个用户的,不能被中间人缓存,应该用于浏览器的私有缓存中,比如:带有HTTP验证信息(帐号密码)的页面 或 某些特定影响状态码的页面*。
“某些特定影响状态码的页面”指具有特殊语义的状态码,比如:
(1)401 Unauthorized
当访问受保护的资源时,如果用户未通过身份验证,服务器可能返回401状态码。
此时通常只应允许客户端缓存该响应,因为该内容对其他用户是不适用的。
(2)403 Forbidden
表示服务器理解请求但拒绝执行它。
在某些情况下,公共内容也可能出于安全考虑而不被中间缓存,因此可标记为 private。
(2)404 Not Found
如果某资源不存在,虽然它是公开的,但可能在某些应用场景下不希望被中间缓存。
(3)500 Internal Server Error
表示服务器出现了问题,返回的内容可能不稳定,因此不应被中间缓存。
private 使用场景
(1)用户特定数据
如个性化推荐、用户历史记录等,仅对特定用户合适,其他用户无法访问,即便返回成功状态,为了保护用户隐私,建议使用 private。
(2)临时性数据
如操作结果、表单提交后给予反馈的信息等,这类信息通常只需当前用户知道,对其他用户无意义,适合标记为 private。
- Title: 浏览器缓存与304状态码
- Author: Zoella
- Created at : 2024-10-16 17:52:10
- Updated at : 2024-10-18 21:42:31
- Link: https://zoella-w.github.io/2024/10/16/24-浏览器缓存与304状态码/
- License: This work is licensed under CC BY-NC-SA 4.0.