max-age采用相对时间机制,从响应发出(服务器)或接收(浏览器)起计算秒数,不依赖时钟同步;Expires为绝对时间戳,易受客户端时间篡改影响,且2026年已无需兼容仅支持Expires的极老客户端。

max-age 用的是“从响应发出那一刻起算多少秒”,不依赖服务器和浏览器各自的时间是否对得上。
max-age 的计时起点分两种情况
对浏览器(私有缓存):从它收到响应的那一刻开始倒计时;
对 CDN 或代理(共享缓存):从服务器生成响应、写入 Date 头的那一刻开始倒计时。
两者都基于实际发生的网络事件,不是靠读取某台机器的系统时钟。
对比 Expires 的绝对时间缺陷
Expires 写的是类似 Expires: Tue, 21 Jul 2026 16:36:00 GMT 这样的固定时间点。
如果用户手动把电脑时间调快 2 小时,浏览器一看“已过期”,立刻发请求——哪怕资源根本没变;
如果调慢 2 小时,缓存就多留 2 小时,用户可能卡在旧版本里。
max-age 怎么绕开这个问题
- 服务器只管发一个数字,比如
Cache-Control: max-age=2592000 - 浏览器收到后,记下当前本地时间,加上 30 天秒数,得出自己的过期时刻
- CDN 收到后,用响应头里的
Date时间 + 30 天秒数,算出自己的过期时刻 - 双方各自算,互不干扰,也不需要校准时钟
为什么现代项目基本不用 Expires
浏览器优先认 Cache-Control,Expires 只为兼容 IE8 等极老客户端;
而 2026 年绝大多数环境已无需考虑这类兼容性,单独设 max-age 就够了;
即使同时写了 Expires,只要 max-age 存在,它就会被忽略。

















