还支持HTTP/2服务器推送。NGINX Plus R15.
我们很高兴地宣布,NGINX 1.13.9已于2018年2月20日发布,并支持HTTP/2服务器推送。对于NGINX Plus用户,即将于2018年4月发布的NGINX Plus R15将支持HTTP/2服务器推送。
服务器推送在HTTP/2规范中定义,允许服务器预先将资源推送到远程客户端,因为预计客户端可能很快会请求这些资源。这样可以将页面加载操作中的RTT(往返时间,即完成一次请求和响应所需的时间)减少1次或更多,从而更快地响应用户。
服务器推送可预先向客户端提供渲染网页所需的样式表、图像及其他资源。请注意仅推送必需的资源,不要推送客户端可能已缓存的资源。
在这篇博文中,我将介绍:
配置HTTP/2服务器推送
要在加载页面的同时推送资源,请按如下方式使用“http2_push”指令:
server { # Ensure that HTTP/2 is enabled for the server
listen 443 ssl http2;
ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;
root /var/www/html;
# whenever a client requests demo.html, also push
# /style.css, /image1.jpg and /image2.jpg
location = /demo.html {
http2_push /style.css; http2_push /image1.jpg; http2_push /image2.jpg;
}
}验证HTTP/2服务器推送
可以使用以下两种方法中的任意一种,轻松验证服务器推送是否生效:
- Web 浏览器中的开发者工具
- 命令行HTTP/2客户端,例如
nghttp
使用开发者工具验证(Google Chrome)
下面以Google Chrome为例,介绍如何使用Web浏览器中的开发者工具验证服务器推送是否生效。如图所示,Chrome开发者工具中发起程序列位于网络标签页,该列显示,在请求/demo.html时,有多个资源被推送到客户端。

使用命令行客户端验证(nghttp)
除了Web浏览器工具外,还可以使用nghttp2.org项目中的nghttp2.org命令行客户端来验证服务器推送是否生效。可以从nghttp下载该命令行客户端,网址为GitHub;如果有适用于相应操作系统的软件包,也可以直接安装。对于Ubuntu,请使用nghttp2-client软件包。
在输出中,星号(*)表示由服务器推送的资源。
$ nghttp -ans https://example.com/demo.htmlid responseEnd requestStart process code size request path
13 +84.25ms +136us 84.11ms 200 492 /demo.html
2 +84.33ms * +84.09ms 246us 200 266 /style.css
4 +261.94ms * +84.12ms 177.83ms 200 40K /image2.jpg
6 +685.95ms * +84.12ms 601.82ms 200 173K /image1.jpg自动向客户端推送资源
在许多情况下,在NGINX配置文件中列出要推送的资源并不方便,甚至无法实现。因此,NGINX还支持拦截Link预加载标头,然后推送这些标头中指定的资源。要启用预加载,请在配置中加入http2_push_preload指令:
server { # Ensure that HTTP/2 is enabled for the server
listen 443 ssl http2;
ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;
root /var/www/html;
# Intercept Link header and initiate requested Pushes
location = /myapp {
proxy_pass http://upstream;
http2_push_preload on;
}
}例如,当NGINX作为代理运行(处理HTTP、FastCGI或其他流量类型)时,上游服务器可以在响应中添加如下Link标头:
Link: </style.css>; as=style; rel=preloadNGINX会拦截此标头,并开始通过服务器推送/style.css。Link标头中的路径必须是绝对路径;不支持./style.css之类的相对路径。路径可选择包含查询字符串。
要推送多个对象,可以提供多个Link标头;更好的做法是,将所有对象放入以逗号分隔的列表中:
Link: </style.css>; as=style; rel=preload, </favicon.ico>; as=image; rel=preload如果不希望NGINX推送预加载资源,请在标头中添加nopush参数:
# Resource is not pushedLink: </nginx.png>; as=image; rel=preload; nopush启用“http2_push_preload”后,还可以在NGINX配置中设置响应标头,以启动预加载服务器推送:
add_header Link "</style.css>; as=style; rel=preload";选择性地向客户端推送资源
HTTP/2规范并未解决如何确定是否推送资源这一难题。显然,只有在确定客户端可能需要某项资源,并且不太可能已缓存该资源时,才适合向客户端推送。
一种可行的方法是,仅在客户端首次访问网站时向其推送资源。例如,可以检测是否存在会话Cookie,并根据检测结果设置Link标头,使资源仅在不存在会话Cookie时预加载。
假设客户端行为符合预期,并在后续请求中包含该Cookie,则采用以下配置时,NGINX在每个浏览器会话中只会向客户端推送一次资源:
server {
listen 443 ssl http2 default_server;
ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;
root /var/www/html;
http2_push_preload on;
location = /demo.html {
add_header Set-Cookie "session=1";
add_header Link $resources;
}
}
map $http_cookie $resources {
"~*session=1" "";
default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=image; rel=preload";
}衡量HTTP/2服务器推送的效果
为了衡量服务器推送的效果,我们创建了一个简单的测试页面/demo.html,该页面引用了单独的样式表/style.css。该样式表还引用了两张图片。我们使用三种不同的配置测试了页面加载时间:
- 顺序执行的
GET请求(未优化):浏览器发现需要某项资源时才加载该资源 - 预加载提示,预加载提示(
Link标头)包含在首次响应中,用于指示浏览器加载依赖项 - 服务器推送(仅限HTTP/2),依赖项会预先推送到浏览器

我们分别使用HTTP、HTTPS或HTTP/2对每种配置进行了多轮测试。前两种配置适用于这三种协议,而服务器推送仅适用于HTTP/2。
使用Chrome开发者工具测量相关行为。对每种配置最常见的行为进行评估并取平均值,再将测得的时间与链路的RTT(使用ping测量)进行关联,以说明每种方法的机制性影响。

一些基本观察
- DOM 加载是指建立新连接并获取demo.html页面所需的时间。样式表是指获取CSS资源所需的时间。
- 通过HTTP建立连接需要1个RTT;通过HTTPS和HTTP/2建立连接需要2个RTT。
- HTML和CSS资源的有效载荷小于最大传输单元(MTU)的大小,因此一次
GET操作大约可在1 RTT内完成。
结果解读:预加载提示
- 预加载提示对CSS资源的影响很小,因为HTML资源直接引用了该CSS资源,并且HTML资源传输速度很快。HTML页面传输完成后,浏览器会立即发起CSS请求。
- 预加载提示确实可以更快地开始下载CSS资源中声明的资源(此处为两张图片)。使用预加载提示后,下载可提前1个RTT开始。
- 预加载提示带来的净节省时间可达1 RTT或更长。并行下载资源时,浏览器必须再建立一个或多个连接。性能取决于速度较慢的请求(响应较大)是优先调度,还是因建立新连接而延迟。这种不可预测的请求顺序使HTTP和HTTP/2的速度提升1 RTT,HTTPS的速度提升2 RTT。
结果解读:服务器推送
- 与预加载提示相比,服务器推送又将时间缩短了1 RTT。推送“响应”与第一个请求的响应同时启动,而预加载提示响应会产生1 RTT的延迟,其中第一个请求的响应耗时0.5 RTT,预加载
GET请求耗时0.5 RTT。
测试说明
- 每种配置均进行了多次测试。每次测试开始时,浏览器缓存均为空,且尚未与NGINX服务器建立任何keepalive连接。使用NGINX指令
keepalive_timeout和http2_idle_timeout快速关闭keepalive连接。 - 目前似乎无法向Chrome推送字体资源,这可能是由于一个已知问题。即使字体资源已推送,Chrome仍会显式请求该资源。
- 每次测试前均明确清除浏览器缓存,并为所有内容设置已过期的缓存控制标头后再提供。
- Chrome会缓存预加载的资源。禁用缓存后,Chrome并不总是会忽略这些缓存资源;即使明确执行“清除浏览器缓存”操作,也不一定能将其清除。(在Chrome中,一种禁用缓存的方法是:在开发者工具的停用缓存复选框。该复选框位于网络标签页。要清除浏览器缓存,可在开发者工具打开时右键单击浏览器的刷新按钮,然后选择清空缓存并硬性重新加载)。
- 某些内容预加载尝试会导致Chrome无法成功重新验证缓存副本,随后按正常方式下载资源。这些尝试未计入测量结果。
- 对于使用之前已接受的自签名证书的所有新SSL连接,Chrome都会增加不必要的2 RTT延迟。为避免此延迟,测试使用了由CA签名的Let’s Encrypt证书。
- 通过编辑本地/etc/hosts文件解决了DNS延迟问题。
- 这些简单测试并未尝试衡量客户端已有资源缓存副本时服务器推送的效果。测试中,每次测试前都会清除所有缓存,而且大多数推送完成得太快,无法取消。
结论
本测试特意采用简单设计,以突出预加载提示和服务器推送的工作机制。在简单场景下,与预加载提示相比,服务器推送可减少1个RTT;与未经优化的顺序GET请求及依赖资源发现相比,改进幅度更大。
更贴近实际的用例涉及更多变量:多个依赖资源、多个来源,甚至可能因推送已缓存或并非立即需要的资源而浪费带宽。浏览器之间的差异也会影响性能。实际结果必然会与这项简单测试有所不同。
例如,Chrome团队发布了有关何时部署服务器推送的详细建议,并对更复杂的网站进行了测量,以比较不进行优化、使用预加载提示以及通过HTTP/2进行服务器推送的效果。他们发布的《HTTP/2推送经验法则》值得所有考虑在生产环境中部署HTTP/2服务器推送的人员阅读。
实际结论是,如果能提前确定所需资源,让上游服务器发送预加载提示确实有益。推送这些资源带来的额外收益虽小,但可以衡量;不过,这也可能浪费带宽,并延迟所需资源的传输。应仔细测试和监控所有服务器推送配置。
附录:HTTP/2推送如何工作?
以下信息部分基于Jake Archibald在一篇内容非常详尽的博客文章HTTP/2推送比我想象的更棘手中的研究。
HTTP/2服务器推送通常用于在客户端请求资源时,预先发送相关的依赖资源。例如,当客户端请求网页时,服务器可能会向客户端推送该网页依赖的样式表、字体和图像。
当客户端建立HTTP/2连接时,服务器可以选择通过该连接发起一个或多个服务器推送响应。这些推送会发送客户端未明确请求的资源。
客户端可以拒绝推送(通过发送RST_STREAM帧),也可以接受推送。客户端会将推送的内容存储在与HTTP/2连接关联的本地“推送缓存”中。
之后,当客户端通过已建立的HTTP/2连接请求资源时,会检查该连接的推送缓存中是否存在已完成或正在传输的请求响应。客户端会优先使用缓存的资源,而不是为该资源发起新的HTTP/2请求。
所有已推送的资源都会保留在每个连接的推送缓存中,直至(a)该资源被使用,或(b)HTTP/2连接关闭:
- 如果使用该资源,客户端会获取一个副本,并删除推送缓存中的相应条目。如果该资源可缓存,客户端随后可将其副本缓存在HTTP页面缓存中。
- 如果HTTP/2连接因任何原因关闭,其本地推送缓存将被删除。
这会带来以下几方面的影响:
- 浏览器会优先使用HTTP页面缓存中的内容,而不是推送缓存中的内容,即使推送的内容更新。
- HTTP/2连接可在不同的页面加载之间共享。因加载某个页面而推送的资源,可在加载其他页面时收到请求后使用。
- 携带凭据的请求与不携带凭据的请求使用不同的HTTP/2连接。例如,如果某个资源通过跨源请求(携带凭据)推送,而浏览器随后发出不携带凭据的请求来获取该资源,则可能无法找到该资源。
可以在Jake Archibald的博客文章《HTTP/2推送比我想象的更棘手》中查看更详细的问题列表。
HTTP/2服务器推送是一项值得关注的功能。请务必全面测试HTTP/2服务器推送配置;如果预加载提示能够提供更可预测且可感知缓存的行为,请做好改用预加载提示的准备。
关于作者

相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
