Age、Date 与新鲜度计算

Age 和 Date 头部的含义,以及浏览器如何根据 RFC 9111 计算缓存是否新鲜。

#tech / dev / frontend #type / concept #status / evergreen #platform / browser #tech / network

[!info] related notes

Age、Date 与新鲜度计算

一句话定义

Age 告诉浏览器这个响应在缓存中已经待了多久;Date 告诉浏览器这个响应是什么时候由源站生成的。两者配合 max-age 决定缓存还剩多少新鲜时间。

Age 头部

Age: 1800
  • 表示响应从源站生成后,已经在缓存节点(CDN、代理、浏览器)中存在了 1800 秒
  • 每经过一个代理/CDN 节点,Age 值会累加
  • 浏览器收到时,Age 反映的是整条链路上的总缓存时间

代理如何累加 Age

源站 → CDN(缓存 10 分钟) → 浏览器

源站返回 Cache-Control: max-age=3600。CDN 缓存了 10 分钟后浏览器请求命中:

  • CDN 响应 Age: 600(10 分钟 = 600 秒)
  • 浏览器收到后,剩余新鲜时间 = 3600 - 600 = 3000 秒

Date 头部

Date: Thu, 22 May 2026 08:00:00 GMT
  • 表示源站生成响应的时刻
  • 每个 HTTP 响应都应该包含 Date
  • 如果 Date 缺失或与本地时钟偏差过大,新鲜度计算可能不准确

时钟偏移问题

如果源站时钟比浏览器快 5 分钟:

  • 浏览器看到 Date 是”未来”的时间
  • apparent_age 会被算为 0(因为 response_time - date_value < 0
  • 实际上缓存可能已经不新鲜了,但浏览器认为它还很新

新鲜度计算(RFC 9111 Section 4.2)

浏览器判断缓存是否新鲜的简化公式:

freshness_lifetime = max_age - age - response_delay

其中:

变量含义
max_ageCache-Control: max-age=N 中的 N
age响应中的 Age 头部值
response_delay浏览器发出请求到收到响应的耗时(同源通常可忽略)

如果 freshness_lifetime > 0,缓存仍新鲜;否则进入协商缓存。

完整公式(RFC 9111 原文)

apparent_age = max(0, response_time - date_value)
corrected_age_value = age_value + (response_time - request_time)
corrected_initial_age = max(apparent_age, corrected_age_value)
freshness_lifetime = max_age - corrected_initial_age

response_delay = response_time - request_time 是浏览器到上一跳的网络延迟。对于直接从源站拿到的响应,age_value 通常为 0,公式退化为 freshness_lifetime ≈ max_age

实际示例

示例 1:无代理(最常见)

源站 → 浏览器
Cache-Control: max-age=3600
Age: 0(或无 Age 头)

浏览器剩余新鲜时间 ≈ 3600 秒。

示例 2:经过 CDN

源站 → CDN(缓存 10 分钟)→ 浏览器
Cache-Control: max-age=3600
Age: 600

浏览器剩余新鲜时间 = 3600 - 600 = 3000 秒。

示例 3:多级代理

源站 → CDN(5 分钟)→ 公司代理(2 分钟)→ 浏览器
Cache-Control: max-age=3600
Age: 420(5 min + 2 min)

浏览器剩余新鲜时间 = 3600 - 420 = 3180 秒。

启发式新鲜度

当响应既没有 Cache-Control: max-age 也没有 Expires 时,浏览器可以用启发式算法估算新鲜度:

freshness_lifetime = (date_value - last_modified_value) * 10%

这就是为什么有些资源即使没设缓存头,短时间内刷新也不会重新请求——浏览器在用启发式新鲜度。

[!warning] 启发式新鲜度是浏览器的”猜测”行为,不同浏览器实现不同。生产环境应始终显式设置 Cache-Control

常见误解

  • “设置了 max-age=3600 就一定缓存 3600 秒” → 不一定,经过代理后 Age 会缩短实际新鲜时间
  • Age 是浏览器自己加的” → 不是,Age 是缓存节点(CDN/代理)添加的
  • “没有 Age 头就说明没经过缓存” → 不一定,有些代理可能不添加 Age,或者 Age: 0
创建于 2026/5/23 更新于 2026/5/27