2015年5月,HTTP/2作为RFC 7540正式发布。在众多新特性中,Server Push被寄予了最高的期望——它被认为是解决"关键请求链"延迟问题的终极方案。理论上,当浏览器请求HTML时,服务器可以同时推送CSS、JavaScript等资源,将原本需要两个往返时间(RTT)的加载过程压缩为一个。
七年后的2022年10月,Chrome 106默认禁用了Server Push。2024年10月,Firefox 132完全移除了对它的支持。从被寄予厚望到彻底消亡,Server Push只用了不到十年。这不是一个技术被更好方案取代的故事,而是一个关于"理论完美"如何在Web的现实复杂性面前崩溃的案例。
理想的起点:省掉的那个RTT
要理解Server Push为何如此诱人,需要先理解它试图解决的问题。
在传统的HTTP/1.1模型中,页面加载是一个瀑布流过程:浏览器请求HTML → 解析HTML → 发现CSS/JS链接 → 发起新请求 → 下载资源。每次发现新资源都需要一次网络往返,这个延迟在高延迟网络(如移动网络)上尤其明显。
sequenceDiagram
participant Browser
participant Server
Browser->>Server: GET /index.html
Server-->>Browser: 200 OK + HTML
Note over Browser: 解析HTML,发现CSS和JS
Browser->>Server: GET /style.css
Browser->>Server: GET /app.js
Server-->>Browser: 200 OK + CSS
Server-->>Browser: 200 OK + JS
Server Push的核心思想是:服务器比浏览器更清楚页面需要什么资源。当收到HTML请求时,服务器可以主动推送CSS和JS,而不必等待浏览器解析HTML后才发现它们。
sequenceDiagram
participant Browser
participant Server
Browser->>Server: GET /index.html
Server-->>Browser: PUSH_PROMISE /style.css
Server-->>Browser: PUSH_PROMISE /app.js
Server-->>Browser: 200 OK + HTML
Server-->>Browser: (Push) style.css
Server-->>Browser: (Push) app.js
Note over Browser: 资源已在本地,无需额外请求
理想情况下,这省掉了一个完整的RTT——在100ms延迟的网络上是100ms,在200ms延迟的移动网络上就是200ms。这正是Server Push被热切期待的原因。
第一个裂痕:服务器不知道浏览器有什么
Server Push的设计存在一个根本性的信息不对称:服务器不知道浏览器缓存里有什么。
2017年,Google开发者关系团队的Jake Archibald发表了一篇题为《HTTP/2 push is tougher than I thought》的深度测试文章,揭示了这个问题在实际场景中的严重程度。
当一个用户第二次访问网站时,浏览器缓存里可能已经有了CSS文件。但服务器不知道这一点,仍然会推送这个CSS。更糟糕的是,Server Push的设计让这种"过度推送"很难避免。
Push Cache的位置很尴尬
在浏览器的缓存层次结构中,Push Cache处于一个特殊位置。Yoav Weiss(Chrome团队工程师)在《A Tale of Four Caches》中详细描述了这个层次:
- Memory Cache:存储当前文档生命周期内的资源
- Service Worker Cache:由开发者控制的持久化缓存
- HTTP Cache:遵循HTTP语义的磁盘缓存
- Push Cache:存储未认领的推送流
关键问题在于:Push Cache是最后被检查的缓存。如果HTTP Cache中已经有了某个资源(即使已经过期),浏览器会优先使用它,而忽略服务器推送的新版本。这意味着服务器可能在推送一个浏览器"认为"它不需要的资源。

图片来源: blog.yoav.ws
更糟糕的是,Push Cache与HTTP/2连接绑定。如果连接关闭,Push Cache中的所有资源都会丢失——即使它们是高度可缓存的。对于不稳定的移动网络,这个问题尤为严重。
凭证模式的陷阱
Archibald的测试还发现了另一个令人头痛的问题:HTTP请求的"凭证模式"(credentials mode)会影响Push Cache的匹配。
当浏览器发起一个不带凭证的请求时(比如跨域字体请求),它会使用一个独立的HTTP/2连接。但如果服务器在带凭证的HTML响应中推送了字体,这个推送的资源会存储在与HTML请求相同的连接的Push Cache中——而字体请求根本不会访问这个缓存。
// 跨域字体请求默认不带凭证
@font-face {
src: url('https://fonts.example.com/font.woff2');
}
// 这个请求会使用独立的HTTP/2连接
// 因此无法匹配HTML响应中推送的字体
解决方案是让CSS链接也使用crossorigin属性,但这需要开发者对HTTP/2的内部机制有深入理解——这与Server Push"简单易用"的设计初衷背道而驰。
第二个裂痕:浏览器实现各行其是
Server Push在规范层面定义清晰,但在浏览器实现层面却各行其是。Archibald的测试揭示了令人沮丧的兼容性问题:
| 行为 | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| fetch()使用Push Cache | ✅ | ✅ | ❌ | ❌ |
| XHR使用Push Cache | ✅ | ✅ | ❌ | ✅ |
<link>使用Push Cache |
✅ | ✅ | ❌ | ✅ |
<script>使用Push Cache |
✅ | ✅ | ❌ | ✅ |
正确处理Vary头 |
❌ | ❌ | ✅ | ❌ |
Safari的问题最为严重。由于它委托给OS X的网络栈(这是一个闭源组件),Push Cache的匹配行为几乎是随机的——有时能用,有时不能用。Archibald发现Safari会创建过多的连接,导致推送的资源分散在不同的连接上,后续请求可能恰好落在一个没有该资源的连接上。
Edge(当时的EdgeHTML版本)则完全不允许fetch()和XMLHttpRequest使用Push Cache。
这些兼容性问题意味着:如果你想使用Server Push,要么接受某些浏览器用户的体验变差,要么进行复杂的UA检测和条件推送。这大大提高了部署成本。
第三个裂痕:Cache Digests的未竟之业
Server Push的核心问题——服务器不知道客户端缓存状态——并非没有解决方案。
2016年,Kazuho Oku(H2O服务器的作者)和Mark Nottingham提出了Cache Digests提案。核心思想是:浏览器使用布隆过滤器(Bloom Filter)编码其缓存内容,在HTTP/2连接建立时发送给服务器。服务器据此判断哪些资源需要推送,哪些可以跳过。
Client → Server: SETTINGS帧 + Cache Digest(布隆过滤器)
Server: 解析Digest,发现客户端已有style.css
Server: 仅推送客户端没有的资源
布隆过滤器的特性使其非常适合这个场景:
- 空间效率高:每个URL+ETag组合只需要约10位
- 没有假阴性:如果Digest说客户端没有某个资源,那就一定没有
- 有假阳性:可能误判客户端已有某个资源(但这只是导致不推送,不会造成错误)
2016年的性能日历文章《Cache Digests: Solving the Cache Invalidation Problem of HTTP/2 Server Push》详细分析了这个方案的优势:
| 场景 | Asset Bundling | Server Push | Server Push + Cache Digest |
|---|---|---|---|
| 冷缓存延迟 | ❌ 多次RTT | ✅ 单次RTT | ✅ 单次RTT |
| 热缓存延迟 | ✅ 单次RTT | ❓ 可能浪费带宽 | ✅ 单次RTT |
| 温缓存延迟 | ❌ 可能多次RTT | ❓ 不确定 | ✅ 单次RTT |
| 冷缓存带宽 | ✅ 最优 | ✅ 最优 | ✅ 最优 |
| 热缓存带宽 | ✅ 最优 | ❌ 可能重复推送 | ✅ 最优 |
然而,Cache Digests最终未能成为标准。Chrome团队的Brad Lassey在移除Server Push的声明中明确指出:
“Cache Digest has never been finished and implemented”
没有浏览器实现了原生的Cache Digests支持,服务器端也缺乏相应的部署。Server Push失去了它最关键的补丁。
数据说话:Chrome移除决策的背后
2020年11月,Chrome团队发布了"Intent to Remove: HTTP/2 and gQUIC server push"的公告。公告中的数据令人震惊:
“Over the past 28 days, 99.95% of HTTP/2 connections created by Chrome never received a pushed stream.”
更糟糕的是:
“Less than 40% of received pushes are used, down from 63.51% two years ago. The rest are invalid, never get matched to a request, or already in cache.”
这意味着即使服务器推送了资源,也有超过60%是浪费的。Akamai在2019年的研究也得出了类似的结论:Server Push要么对性能没有影响,要么只有边缘改善——而且这还是在经过精心调优的情况下。
RWTH Aachen大学2018年发表在CoNEXT'18的论文《Is the Web ready for HTTP/2 Server Push?》提供了更系统性的分析。研究团队构建了一个测试平台,可以可控地重放真实网站的加载过程,测试不同的推送策略。
他们的发现包括:
-
“推送所有对象"策略对性能有害:在测试的top-100网站中,只有58%受益于推送所有对象;在random-100网站中,这一比例只有45%。
-
推送图片几乎总是坏事:图片不参与DOM或CSSOM的构建,推送它们只会与关键资源争夺带宽。
-
最佳推送策略高度依赖网站结构:没有"一刀切"的解决方案。推送什么、推送多少、按什么顺序推送,都需要根据具体网站进行分析。
-
即使是手动优化的推送策略,也难以获得显著改善:研究团队尝试了多种"智能"策略(包括他们自己提出的"交错推送"方案),但对于大多数网站,改善幅度有限。
论文的结论很坦率:
“The question here is not if the Web is ready for Server Push but if the web engineers are eager to manual tuning.”
为什么服务器不推送?
Server Push使用率低(0.04%的连接使用过)的另一个原因是:正确实现推送很困难。
Cloudflare在2016年宣布支持Server Push时,指出了几个关键挑战:
-
只能推送同源资源:如果网站依赖第三方CDN的CSS/JS/字体,Server Push无能为力。现代网站的第三方资源占比往往超过50%。
-
难以判断推送边界:推送太少,收益有限;推送太多,浪费带宽。如何确定"刚刚好”?
-
动态内容难以处理:对于动态生成的HTML,服务器可能不知道页面会引用哪些资源。
-
与现有优化技术冲突:如果网站已经使用了HTTP/1.1时代的优化技术(如CSS精灵图、JS打包),Server Push的优势会被削弱。
Fastly的CDN专家Andrew Betts在一次演讲中指出,Server Push的复杂性主要在于它要求服务器具有"预知能力"——知道浏览器即将请求什么,同时又要知道浏览器已经有了什么。这两个要求在当前的Web架构下都很难满足。
103 Early Hints:务实的替代方案
Server Push消亡的同时,一个更务实的替代方案正在兴起:103 Early Hints。
103 Early Hints是一个信息性HTTP状态码,允许服务器在生成最终响应之前,先发送一个"早期提示"响应,告诉浏览器应该预加载或预连接哪些资源。
Browser → Server: GET /index.html
Server → Browser: 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
[服务器继续处理请求...]
Server → Browser: 200 OK + HTML
与Server Push的关键区别在于:浏览器决定是否实际下载这些资源。
如果浏览器缓存里已经有了style.css,它会忽略Early Hints中的预加载建议。这解决了Server Push的核心问题——服务器不知道客户端有什么。
Fastly在2020年的实验数据显示,Early Hints在某些场景下可以改善数百毫秒的渲染时间。Chrome团队在2022年正式支持了这一特性,并在文档中指出:
“Early Hints works better in practice since it combines the ability to send a preliminary response with hints that leave the browser in charge of fetching or connecting to what it actually needs.”
Early Hints的另一个优势是它可以利用服务器的"思考时间"。当服务器需要访问数据库或进行复杂计算才能生成HTML时,这段"思考时间"原本是网络空闲的。Early Hints允许浏览器在这段时间内开始预加载关键资源。

图片来源: developer.chrome.com
当然,Early Hints也有局限性:它仍然需要浏览器发起额外的请求,无法像Server Push那样真正节省一个RTT。但Chrome团队的数据表明,一个正确工作的"提示"比一个经常浪费的"推送"更有价值。
技术理想主义的代价
Server Push的故事是一个关于技术理想主义的警示。
从理论角度看,Server Push的设计几乎完美:服务器知道页面需要什么,主动推送资源,省掉一个RTT。但Web的现实远比理论复杂:
- 浏览器有复杂的缓存层次结构
- 网络连接可能随时断开
- 第三方资源占主导地位
- 不同的浏览器有不同的实现
- 开发者没有时间去优化每个页面
Server Push要求服务器"知道"太多它无法知道的事情:客户端的缓存状态、浏览器的实现细节、用户即将访问的页面。在信息如此不对称的情况下,“主动推送"很难比"被动响应"做得更好。
HTTP工作组的Mark Nottingham在2017年的一次讨论中说:
“Server push is meant as a performance optimization, a client is allowed to choose to reject any pushed streams.”
这句话道出了Server Push的本质:它是一个性能优化特性,而非功能性特性。当一个性能优化特性在实际使用中可能降低性能时,它的存在本身就值得质疑。
2024年,当Firefox最终移除Server Push支持时,这项技术正式成为了Web历史的一部分。它留下了一笔遗产:对"主动推送"这个概念的探索,以及对Web缓存机制更深入的理解。而它留下的空白,正被103 Early Hints和更智能的预加载策略填补。
这不是一个失败的故事——这是一个关于Web社区如何学习、调整、迭代的故事。Server Push或许没有实现它的承诺,但它帮助我们更好地理解了Web性能优化的边界。
参考来源
-
Jake Archibald. “HTTP/2 push is tougher than I thought.” https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/
-
Torsten Zimmermann, et al. “Is the Web ready for HTTP/2 Server Push?” CoNEXT'18. https://arxiv.org/pdf/1810.05554
-
Brad Lassey. “Intent to Remove: HTTP/2 and gQUIC server push.” Chrome Blink Dev. https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYLvmQUBY
-
Yoav Weiss. “A Tale of Four Caches.” https://blog.yoav.ws/posts/tale-of-four-caches/
-
Sebastiaan Deckers. “Cache Digests: Solving the Cache Invalidation Problem of HTTP/2 Server Push.” Performance Calendar 2016. https://calendar.perfplanet.com/2016/cache-digests-http2-server-push/
-
Chrome Developers. “Faster page loads using server think-time with Early Hints.” https://developer.chrome.com/docs/web-platform/early-hints
-
Cloudflare. “Announcing Support for HTTP/2 Server Push.” https://blog.cloudflare.com/announcing-support-for-http-2-server-push-2/
-
Fastly. “Exploring 103 Early Hints Beyond Server Push.” https://www.fastly.com/blog/beyond-server-push-experimenting-with-the-103-early-hints-status-code
-
竹林里有冰. “HTTP/2 Server Push Has Effectively ‘Died’ - I Miss It.” https://zhul.in/en/2025/11/05/http-2-server-push-is-practically-obsolete/
-
M. Belshe, R. Peon, M. Thomson. “Hypertext Transfer Protocol Version 2 (HTTP/2).” RFC 7540. https://httpwg.org/specs/rfc7540.html