浏览器缓存与304状态码

Zoella Lv4

总结

出现304状态码的条件

  • 不禁止缓存
  • 不检查本地缓存是否过期;或者检查本地缓存、且未过期
  • 响应头有Etag(则下次的请求头中有If-None-Match);或者响应头有Last-Modified(则下次的请求头中有If-Modified-since
  • 缓存有效

注意:下图中的EtagLast-Modified之间不存在先后顺序

图1

一、304请求的交互过程

假设用户通过浏览器访问某静态资源(比如图片image.png),且该资源之前已被浏览器缓存,现在分析两次请求的交互过程。

第一次请求(初始加载)

1、浏览器发送请求

用户在浏览器中输入 URL,浏览器向服务器发送 GET 请求,获取 image.png。
请求头可能如下:

1
2
3
4
GET /image.png HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 ...
Accept: image/webp,image/apng,image/*,*/*;q=0.8

2、服务器响应

服务器处理请求,并返回 200 OK 响应,同时附带以下响应头:

1
2
3
4
5
6
HTTP/1.1 200 OK
Content-Type: image/png
Content-Length: 123456
Last-Modified: Mon, 01 Jan 2023 12:00:00 GMT
ETag: "abc123"
Cache-Control: max-age=31536000
  • Last-Modified:表示资源最后修改的时间
  • ETag:资源的唯一标识符
  • Cache-Control:指示浏览器可以缓存该资源的时长

3、浏览器缓存资源

浏览器接收响应后,将image.png缓存到本地,并根据响应头信息(如Last-ModifiedETag)记录该资源的状态

第二次请求(缓存验证)

1、浏览器发送条件请求

当用户再次访问同一资源,浏览器会检查缓存,如果该资源仍然有效,就发送一个带条件的 GET 请求到服务器,请求头如下:

1
2
3
4
5
GET /image.png HTTP/1.1
Host: example.com
If-Modified-Since: Mon, 01 Jan 2023 12:00:00 GMT
If-None-Match: "abc123"
User-Agent: Mozilla/5.0 ...
  • If-Modified-Since:告诉服务器,如果在指定日期之后资源被修改,则返回新的资源
  • If-None-Match:告诉服务器,如果资源的ETag与提供的值不匹配,则返回新的资源

2、服务器处理请求

服务器接收到条件请求后,检查该资源的状态:
如果资源自Last-Modified时间以来未被修改,且ETag匹配,那么服务器不会返回新的内容,而是返回 304 Not Modified。

服务器返回如下响应:

1
2
HTTP/1.1 304 Not Modified
Date: Mon, 02 Jan 2023 12:00:00 GMT

注意:304 响应没有消息体,通常只包含必要的状态码和一些响应头。

3、浏览器处理304响应

当浏览器接收到 304 Not Modified 响应时,了解到之前缓存的 image.png 仍然有效,因此直接使用缓存中的版本,而无需重新下载。

二、详细讨论

1、是否禁止缓存

禁止缓存:缓存中不得存储任何关于客户端请求和服务端响应的内容,客户端每次发起的请求都会下载完整的响应内容

在请求头中,Cache-Control: no-storePragma: no-cache 都可以禁止缓存

  • Pragma: no-cache 可用于 http 1.0http 1.1
  • Cache-Control: no-store 只能用于 http 1.1

2、是否检查本地副本是否过期

Cache-Controlno-cachemust-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,通过比较ExpiresDate的值来判断是否缓存有效:

图2

(3)Last-Modified

表示资源的最后修改时间。
如果无max-ageexpires但有Last-Modified,缓存的时长等于

1
(Date - Last-Modified) / 10

图3

如果都没有,浏览器将不会对该需求进行缓存。

4、本地副本没过期

直接从缓存中读取资源,并返回200状态码。

图4

5、本地副本已过期

进行「源服务器进行有效性校验」的前期准备:

(1)If-None-Match

首先,在请求头里寻找If-None-Match字段(其值为服务器上次返回的ETag响应头的值)

图5
图6

(2)If-Modified-Since

如果没有If-None-Match,则在请求头中寻找If-Modified-Since字段(其值为服务器上次返回的Last-Modified响应头中的日期值)

图7
图8

如果If-None-MatchIf-Modified-Since都没有,则直接向服务器请求数据

6、去源服务器进行有效性校验

如果请求头中有If-None-MatchIf-Modified-Since,则到源服务器进行有效性校验。
如果源服务器资源没有变化,则返回304;如果有变化,则返回200。

7、上述流程的总结图

图9

三、意义

  • 避免了不必要的数据传输,提高了网络效率和用户体验
  • 在频繁访问相同资源的情况下,可以显著降低带宽消耗和服务器负载

四、补充

私有缓存和公共缓存

Cache-Control还有两个值:privatepublic

  • 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.