ServerPush(服务端推送)是HTTP/2协议提供的一项特性,它改变了传统模式下客户端发起请求、服务器被动响应的交互方式。在HTTP/2环境下,服务器可以在响应主文档的同时,主动将页面即将用到的静态资源一并推送给浏览器,浏览器收到后先放入本地缓存,等解析HTML真正需要这些资源时,直接从缓存读取,省去了一次额外的请求往返。Apache从2.4.x版本开始,通过mod_http2模块对这项特性提供了完整支持。本文将从原理、配置实践和常见问题三个层面,详细讲解如何在Apache中使用ServerPush。

一、ServerPush的工作原理
要理解ServerPush,首先要明白它解决的是什么问题。在传统的HTTP/1.1模式下,浏览器获取一个页面的流程是:先请求HTML文档,解析到CSS或JS引用后再发起资源请求,每次请求都需要一次网络往返(RTT)。即使浏览器做了域名分片、连接复用等优化,资源请求依然必须等待HTML解析完成之后才能发出,这段时间是白白浪费的。
HTTP/2的ServerPush把这个时序打破了。当服务器判断客户端很可能需要某个资源时,会在同一个TCP连接上以PUSH_PROMISE帧的形式告知客户端:我准备给你推送这个资源了。客户端收到PUSH_PROMISE后,可以选择接受或拒绝(通过RST_STREAM帧)。如果接受,服务器随后把资源内容通过新的流发送过来,浏览器将其缓存备用。
需要注意的是,ServerPush并不等于WebSocket那样的双向实时通信。它本质上仍然是HTTP请求响应模型的增强,服务器只是提前响应了客户端尚未发出的请求。推送的资源必须与主请求在同一个HTTP/2连接上传输,这也意味着如果你的站点还在使用HTTP/1.1,这项特性完全无法生效。
二、Apache中启用和配置ServerPush
Apache对HTTP/2和ServerPush的支持由mod_http2模块提供。首先确认模块已加载,在httpd.conf或对应的配置文件中检查是否有类似配置:
LoadModule http2_module modules/mod_http2.so Protocols h2 h2c http/1.1
其中Protocols指令声明了协议协商优先级,h2表示基于TLS的HTTP/2,h2c表示明文HTTP/2。由于浏览器只支持加密的HTTP/2,生产环境必须启用HTTPS才能让ServerPush工作。
配置好HTTP/2之后,推送资源主要通过H2Push指令和Link响应头配合完成。最典型的用法是在响应头中声明要推送的资源:
<FilesMatch "\.html$">
Header add Link "</style.css>; rel=preload; as=style"
Header add Link "</app.js>; rel=preload; as=script"
H2Push on
</FilesMatch>这段配置的含义是:对所有HTML文件的响应,追加两个Link头,分别声明预加载样式表和脚本。当浏览器通过HTTP/2访问该页面时,Apache会解析这些Link头,发现rel=preload的声明后,自动将对应资源推送给客户端。H2Push on是全局开关,也可以放在具体的VirtualHost或Directory块中做更细粒度的控制。
除了Link头,Apache还提供了H2PushResource指令,可以直接在服务器配置层面指定推送资源,不依赖响应头,适合推送全站通用的公共资源:
<VirtualHost *:443>
ServerName www.ipipp.com
Protocols h2 http/1.1
H2Push on
H2PushResource /css/common.css
H2PushResource /js/jquery.min.js
</VirtualHost>需要注意优先级问题:Link响应头的优先级高于H2PushResource,两者同时存在时以Link头为准。此外,被推送的资源本身也可以继续触发推送,形成链式推送,但要谨慎使用,容易造成资源浪费。
三、推送的缓存匹配与条件请求问题
ServerPush最容易被忽视的坑在于缓存判断。服务器在决定推送某个资源时,并不知道浏览器本地缓存里是否已经有了这个文件。如果客户端缓存中已经存在该CSS,服务器仍然推送一份,不仅没有收益,反而浪费了带宽,甚至可能挤占连接上其他更紧急的请求。
Apache提供了H2PushDiarySize指令来缓解这个问题。它会在服务端维护一份推送日记,记录已经向该客户端推送过的资源指纹(基于摘要算法),后续请求到来时,Apache会检查日记,避免重复推送。默认值为256,单位是条目数:
H2PushDiarySize 512 H2PushDiaryType light
H2PushDiaryType指定日记的摘要算法,light表示轻量级摘要,full表示完整摘要。日记越大占用的内存越多,但判断越精确,需要根据站点资源数量权衡。
另一个相关问题是推送资源与浏览器缓存验证的配合。浏览器收到推送资源后,如果在缓存中已有同URL资源且仍然新鲜,它会发送RST_STREAM拒绝推送。这要求推送的资源最好配置合理的缓存策略,比如为静态资源设置较长的max-age和版本号化的文件名,这样推送日记和浏览器缓存才能配合得当,避免反复推送旧版本文件。
四、性能权衡与关闭推送的策略
ServerPush并非总是正向收益。经验表明,如果推送的资源体积过大、客户端带宽有限,或者推送了页面根本不会用到的资源,整体加载时间反而会变长。特别是移动端场景,推送大量未压缩的大文件可能直接阻塞连接上的关键请求。
建议推送的资源满足几个条件:体积小(几十KB以内)、几乎每个页面都会用到、位于首屏渲染关键路径上。典型的就是站点公共样式表、字体文件和核心JS库。对于图片这类大体积资源,一般不建议推送。
在某些场景下你可能希望显式关闭推送。比如检测到客户端使用HTTP/1.1时(推送本就不会发生),或者针对特定路径禁用:
<Location "/download/">
H2Push off
</Location>此外,如果经过压测发现推送收益不明显,也可以全局关闭,改用传统的preload方式,让浏览器自己根据Link头提前发起请求。HTTP/2推送和preload各有适用场景:preload由浏览器自主决策,判断更智能;ServerPush由服务器控制,时机更早。两者并不冲突,可以结合使用。
最后提醒一点,Chrome等浏览器在推出Early Hints(103状态码)等新机制后,对ServerPush的客户端支持策略有所调整,部分浏览器甚至默认禁用了HTTP/2推送。因此在实际部署前,务必确认目标用户群体的浏览器支持情况,并通过Chrome DevTools的Network面板观察推送是否真正生效、缓存命中率是否提升,用数据驱动最终的配置决策。
Apache ServerPush服务端推送HTTP/2修改时间:2026-08-31 19:30:39