Expires (http1.0)
在HTTP Header中通过Expires字段 传递一个GMT格式的字符串。
举例:
Expires: Wed Feb 20 2019 11:25:41 GMT
Cache-Control: max-age=<seconds> (http1.1)
在HTTP Header中通过Cache-Control字段中的值max-age传递一个max-age=<seconds>的字符串。
举例:
Cache-Control: max-age=3600
如果Expires和Cache-Control: max-age=<seconds>二者同时存在,max-age 的优先级高于 expires
(浏览器会根据 max-age 指令的值来决定缓存的有效期,而忽略 Expires)
上面这段话是在中文互联网上出现的很多的内容,表述可能不一样,但是内容就是这些了。
那么浏览器到底怎么判断缓存过期?用max-age判断缓存过期是否与本地时间有关?
在中文互联网的论坛中,基本都是说Expires字段设置的缓存过期时间会受本地时间影响,max-age设置的缓存过期时间不会受本地时间影响。
-那么在使用max-age设置缓存后,浏览器到底怎么判断缓存过期?判断缓存过期是否与本地时间有关?
看我下面这个问题场景。
如果HTTP响应中包含以下头部:
Date: Wed, 16 Oct 2019 07:42:37 GMT
Cache-Control: max-age=3600
按照中文互联网上的常见表述
"浏览器记录下max-age的时间点是通过在HTTP响应中的Date头部来完成的。Date头部指示了服务器响应请求的时间,它通常以格林威治标准时间 (GMT) 格式表示。"
那么浏览器会计算出资源的有效期截止时间为Wed, 16 Oct 2019 08:42:37 GMT,(即当前时间加上max-age指定的秒数) 。浏览器会记录这个时间点,以便后续请求时判断资源是否过期。
问题:
当浏览器第二次发请求的时候,浏览器是否需要找到一个时间节点T2 来判定T2与 Wed, 16 Oct 2019 08:42:37 GMT的大小关系?
-如果需要T2,那么T2如何取值? (是否受本地时间的影响?)
-如果不需要T2,那么浏览器通过什么来判定这个缓存是否过期?
下面开始进入正题,查证的过程中我发现有不少(错误的)内容在中文互联网上被互相转载,站站相传,属实离谱。
本文引用材料均有出处。各位读者可以点link查看。
以chromium内核的实现为例来说明
1、判断缓存是否过期
https://github.com/chromium/chromium/blob/main/net/http/http_response_headers.cc#L1107-L1114
公式如下:
response_is_fresh = (freshness_lifetime > current_age)
这里有个字段 response_is_fresh (相应依旧新鲜)
字面理解,如果response_is_fresh为true,则表示缓存未过期。
如果保鲜时间(freshness_lifetime) 大于 当前经历时间(current_age),则表示缓存未过期。
再往下看,找到freshness_lifetime和current_age的计算方法。
2、freshness_lifetime的计算 ( 保鲜时间(有效期)的计算 )
https://github.com/chromium/chromium/blob/main/net/http/http_response_headers.cc#L1141-L1163
这段话里明确说明max-age的优先级大于expires
如果有max-age
freshness_lifetime = max_age_value
如果没有max-age
freshness_lifetime = expires_value - date_value
看1152 1153两行
Note that neither of these calculations is vulnerable to clock skew, since all of the information comes from the origin server
"注意,这两种计算方式都不会受到时钟偏差的影响,因为所有信息都来自于源服务器。"
也就是说,不论是用max-age还是expires计算freshness_lifetime,都不会受到时钟偏差的影响。
3、current_age的计算 ( 当前经历时间的计算 )
https://github.com/chromium/chromium/blob/main/net/http/http_response_headers.cc#L1264-L1332
// From RFC 7234 section 4.2.3:
//
// The following data is used for the age calculation:
//
// age_value
//
// The term "age_value" denotes the value of the Age header field
// (Section 5.1), in a form appropriate for arithmetic operation; or
// 0, if not available.
//
// date_value
//
// The term "date_value" denotes the value of the Date header field,
// in a form appropriate for arithmetic operations. See Section
// 7.1.1.2 of [RFC7231] for the definition of the Date header field,
// and for requirements regarding responses without it.
//
// now
//
// The term "now" means "the current value of the clock at the host
// performing the calculation". A host ought to use NTP ([RFC5905])
// or some similar protocol to synchronize its clocks to Coordinated
// Universal Time.
//
// request_time
//
// The current value of the clock at the host at the time the request
// resulting in the stored response was made.
//
// response_time
//
// The current value of the clock at the host at the time the
// response was received.
//
// The age is then calculated as
//
// apparent_age = max(0, response_time - date_value);
// response_delay = response_time - request_time;
// corrected_age_value = age_value + response_delay;
// corrected_initial_age = max(apparent_age, corrected_age_value);
// resident_time = now - response_time;
// current_age = corrected_initial_age + resident_time;
1264行到1305行,这一段注释里的now,对应下面HttpResponseHeaders::GetCurrentAge这个方法中的current_time(L1328)
The term "now" means "the current value of the clock at the host performing the calculation". A host ought to use NTP ([RFC5905]) or some similar protocol to synchronize its clocks to Coordinated Universal Time.
关于now的注释这里写明了
"now"一词表示"执行计算的主机上时钟的当前值"。主机应该使用NTP([RFC5905])或类似协议将其时间与世界时钟同步。
那么current_age的计算是需要依赖于客户端主机的本地时钟的。
回到这个问题
如果HTTP响应中包含以下头部:
Date: Wed, 16 Oct 2019 07:42:37 GMT
Cache-Control: max-age=3600
当浏览器第二次发请求的时候,浏览器是否需要找到一个时间节点T2来判定T2与 Wed, 16 Oct 2019 08:42:37 GMT的大小关系?
-如果需要T2,那么T2如何取值? (是否受本地时间的影响?)
-如果不需要T2,那么浏览器通过什么来判定这个缓存是否过期?
总结下来:
(chromium内核的)浏览器用的是比较freshness_lifetime(约等于max-age,保鲜时间,是一个时间跨度)和current_age(当前经历时间,也是一个时间跨度)来计算有效期。
不需要时间节点T2。
计算freshness_lifetime用的是服务器时间, 不受本地时间影响。
但计算current_age用到了本地时间(current_time, 也就是注释中的now),而且没法保证本地时间与服务器时间一致。
因此,不论是Expires还是Cache-Control: max-age=<seconds> 计算缓存有效期的时候,都会受到本地时间的影响。
同步更新到自己的语雀,昨天(2023-10-23)下午语雀崩了,笑哈哈。
https://www.yuque.com/dirackeeko/blog/qun7u06okedz4yck
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。