Nginx 1.13.9 这个版本是在 2018 年 2 月 20 日发布的,包含了对 HTTP/2 服务器推送的支持。对于 NGINX Plus 用户,也会在 2018 年 4 月即将推出的 NGINX Plus R15
版本中包含 HTTP/2
服务器推送支持。
服务器推送 ( 在 HTTP/2 规范 中定义 )允许服务器先发制人地将资源推送到远程客户端,预计客户端可能很快就会请求这些资源。通过预先推送资源,我们可以在一个 RTT 或更多 RTT 页面加载操作中减少 RTT 的数量 ( 往返时间 - 请求和响应所需的时间 ) ,从而为用户提供更快的响应.
服务器推送可以预先给客户端发送样式表,图像和呈现网页所需的其它资源。而我们应该注意只推送所需的资源;不要推送客户端可能已经缓存的资源
本章节,我们会学习 Nginx 1.13.9 版本中提供的 HTTP/2 服务器推送知识的方方面面,包括配置和一些推送指令。
配置 HTTP/2 服务器推送
如果要将资源与请求的页面一起推送,则需要使用 http2_push 指令。使用方法如下
server { # 确保服务器开启了 HTTP/2 支持 listen 443 ssl http2; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # 当客户端请求 index.html 时,同时推送 # /img1.jpg /img2.jpg # 资源给客户端 location = /index.html { http2_push /img1.jpg; http2_push /img2.jpg; } }
http2_push 指令
http2_push 指令用于推送一个资源给远程的客户端,该指令有且只能有一个参数,那就是资源的请求路径 ( 相对于当前的 location
的目录 )
http2_push path_to_resource;
检查 HTTP/2 推送是否成功
我们可以简单的使用下面两种方法来验证刚刚我们配置的服务器推送是否有效
- 现代浏览器中的开发者工具,特别是 Google Chrome 开发者工具中的 「 网络 」 面板
- HTTP/2 命令行工具,例如
nghttp
使用开发者工具 ( Google Chrome ) 验证
我们以 Google Chrome 浏览器为例,介绍如何使用 Web 浏览器中的 「 开发者工具 」验证服务器推送是否生效
下图中, Chrome 「 开发者工具 」中的 「 网络 」 选项卡上的 「 启动器 ( Initiator ) 」 列表示,作为 /index.html
请求的一部分,已将多个资源推送到客户端
使用命令行工具 ( nghttp ) 验证
除了 Web 浏览器工具,我们还可以使用 nghttp2.org 项目中的nghttp命令行客户端来验证服务器推送是否生效。
您可以从 GitHub 下载 nghttp 命令行客户端,或者在可用的情况下安装相应的操作系统软件包
- 对于 Ubuntu,请使用
nghttp2-client
软件包 - 对于 Mac,可以使用
brew install nghttp2
安装
使用方式一般如下
nghttp -ansy https://localhost/index.html
输出结果一般如下
[yufei@localhost htdocs]$ nghttp -ansy https://localhost/ ***** Statistics ***** Request timing: responseEnd: the time when last byte of response was received relative to connectEnd requestStart: the time just before first byte of request was sent relative to connectEnd. If '*' is shown, this was pushed by server. process: responseEnd - requestStart code: HTTP status code size: number of bytes received as response body without inflation. URI: request URI see http://www.w3.org/TR/resource-timing/#processing-model sorted by 'complete' id responseEnd requestStart process code size request path 2 +28.44ms * +4.88ms 23.56ms 200 56K /img1.jpg 13 +29.75ms +501us 29.25ms 200 255 / 4 +44.22ms * +4.92ms 39.30ms 200 53K /img2.jpg
输出结果中,带星号 ( *
) 标记的是服务器推送的资源
自动将资源推送到客户端
在某些情况下,使用 http2_push
指令在 NGINX 配置文件中列出希望推送的资源是不方便的,甚至是不可能的
出于这个原因,Nginx 还支持拦截 Link
预加载响应头的约定,然后推送这些响应头中标识的资源。
要启用预加载,需要在配置文件中添加 http2_push_preload
指令
server { # 确保服务器开启了 HTTP/2 支持 listen 443 ssl http2; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # 拦截 Link 响应头部字段,然后和配置服务器推送 location = /myapp { proxy_pass http://upstream; http2_push_preload on; } }
例如,当 nginx 作为代理 ( 用于 HTTP,FastCGI 或其他流量类型 ) 运行时,上游服务器可以将这样的 Link
响应头添加到其响应中
Link: </img1.jpg>; as=image; rel=preload
nginx 会拦截此头部字段并开启服务器推送 img1.jpt
,Link
响应头中的资源路径必须是绝对路径,不支持 ./img1.jpg
之类的相对路径。同时,路径中还可以添加一些查询字符串
如果要推送多个资源,可以添加 Link
响应头字段多次
Link: </img1.jpg>; as=image; rel=preload Link: </img2.jpg>; as=image; rel=preload
更好的做法,是将这些资源放在一个 Link
头部中,并使用逗号分割
Link: </img1.jpg>; as=image; rel=preload,</img2.jpg>; as=image; rel=preload
如果不想要 nginx 推送某个预加载资源,可以,可以在 Link
头部字段中针对那个资源添加 nopush
参数,就像下面这样
Link: </img2.jpg>; as=image; rel=preload; nopush
当启用 http2_push_preload
后,我们还可以通过在 nginx 配置中设置响应头来启动预加载服务器推送
add_header Link "</img1.css>; as=image; rel=preload";
有选择地将资源推送给客户
显然的,HTTP/2 协议规范并没有确定,也没有推荐哪些资源是可推送的,哪些资源是不可推送的。因此,我们只能添加一些以前实践得来的经验
对于某些资源,如果我们知道客户端可能需要它们,但它们又不太可能已经被缓存,那么这些资源就是可推送的。
另一种可能的方式是仅在首次访问网站时将资源推送到客户端。例如,我们可以测试是否存在某个指定的会话 cookie,仅在会话 cookie 不存在时才预加载资源并有条件地设置 Link
响应头
假设客户端一直运作良好并且在后续的请求中都包含 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 = /index.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=style; rel=preload"; }