nginx开启http2(server push)支持
Table of Contents
http2有什么优点?
一个字:快!
http2主要特性:
- 二进制分帧
- 首部压缩
- 流量控制
- 多路复用
- 请求优先级
- 服务器推送
nginx开启http2前提条件
1、必须配置https证书,RFC并没有规定http2一定要使用https,但目前的浏览器都要求必须使用https。
2、nginx版本必须1.13.9以上(含),在Changes with nginx 1.14中搜索”1.13.9″即可看到:
3、nginx编译时必须加上ngx_http_v2_module
模块,如果你之前已经安装了nginx,可使用nginx -V
来查看是否带有该模块:
4、编译时openssl必须在1.0.2版本以上,运行以下命令即可查看openssl版本:
openssl version
如果你之前已经安装nginx,可用nginx -V
查看所使用的openssl版本,它会有“built with OpenSSL x.x.x”这样的信息,其中“x.x.x”就是版本号,比如“1.1.1b”就超过“1.0.2”就可以了。
有人可能不明白,nginx支持http2跟openssl有什么关系?因为开启http2必须要使用https,而https就是http+tls,而openssl是用于支持tls协议的。
什么是server push?
server push就是服务器推送,就是在用户首次访问你的网站时,把所有需要的资源直接推送到浏览器,这样浏览器就不需要再次发起请求去获取这些资源了,资源包括:css文件、js文件、图片文件、字体文件(反正就是静态文件),不使用server push的情况下,这些文件都是由浏览器主动发起请求去获取的,每一次请求都要三次握手建立连接需要花费时间,当文件很多的时候就会慢,像css加载图片的时候,我们经常把多个小图片合并到一个图片文件里,这也是为了减少浏览器发出多个请求去分别请求这些图片。
未开启server push:
开启server push后:
nginx开启http2
该配置并非完整配置,只是告诉你,在原来已经正常使用https的配置的基础上,在listen后面加个http2
就开启http2了:
server {
# listen在原https配置文件基础上添加http2
listen 443 ssl http2;
……
}
开启http2之后,protocol就会显示h2
了,没开的话它是显示http/1.1
的:
根据这个就可以判断别的网站有没有开启http2,百度的除了cdn资源(js/css/image)用了http2,主站还是用的http/1.1:
而google,所有都用了http2,并且还有些是h2+quic/46,其中quic(Quick UDP Internet Connection)是http3.0的基础,是google研发出来的技术,差距就是这么大,一个连http2都没用上,一个却已经在用http3了(并且是自主研发的):
nginx开启server push方式一
前面只是开启了http2,但还未开启http2的server push功能,该方式很简单,就是使用http2_push
指定要推送哪个文件即可(“/style.css”的“斜杠”是指根目录,这个根目录是指root指定的那个目录,并非Linux服务器的根目录):
location / {
root /usr/share/nginx/html;
index index.html index.htm;
http2_push /style.css;
http2_push /example.png;
}
不过这有两个缺点:
- 1、静态文件很多,每次添加了都要改nginx配置文件并重启,非常不方便;
- 2、访问网站的任何一个网页都会执行
http2_push
,都会推送一次(即使已经推送过了也会重新推送),虽然浏览器接收到这个推送时会先检查本地是否已经缓存了这个文件,如果已经缓存了就会告诉nginx不要再推送,但即使这样,也已经推送了一部分了,这会造成浪费(因为这是不必要的推送)。
当然这两个缺点是可以通过cookie来解决,在配置文件中也能做到,可参考:Nginx 下实现 HTTP/2 服务器推送 (Server Push) 教程。
nginx开启server push方式二(推荐)
方式一非常不方便,并且还有两个缺点,方式二就可以解决这两个缺点。方式二是nginx不直接指定要推送哪个文件,而是直接用http2_push_preload on
开启push功能:
server {
# listen在原https配置文件基础上添加http2
listen 443 ssl http2;
# 添加一句http2_push_preload on表示开启server push功能
http2_push_preload on;
……
}
然后可以通过两种方法告诉nginx要推送哪个文件,通过http header或html页内link标签告诉服务器要推送哪些文件。
方法一:通过http header
告诉nginx推送/styles.css
这个css文件
link: </styles.css>; rel=preload; as=style
告诉nginx推送/script.js
这个js文件
link: </script.js>; rel=preload; as=script
告诉nginx推送/example.png
这个图片文件文件
link: </example.png>; rel=preload; as=image
告诉nginx推送 “https://fonts.example.com/font.woff2” 这个字体文件,并且该字体文件不在本服务器上,而是跨域获取
link: <https://fonts.example.com/font.woff2>; rel=preload; as=font; crossorigin; type="font/woff2"
其实多个Link也可以合并,把多个值用逗号分隔即可:
link: </styles.css>; rel=preload; as=style, </script.js>; rel=preload; as=script, </example.png>; rel=preload; as=image, <https://fonts.example.com/font.woff2>; rel=preload; as=font; crossorigin; type="font/woff2"
怎样发送这个header呢?由后端语言来发送,以下举例php的方法:
<?php
header("link: </styles.css>; rel=preload; as=style", false);
header("link: </script.js>; rel=preload; as=script", false);
header("link: </example.png>; rel=preload; as=image", false);
header('link: <https://fonts.example.com/font.woff2>; rel=preload; as=font; crossorigin; type="font/woff2"', false);
?>
当浏览器请求nginx时,如果是php文件,nginx会把请求交给php-fpm来解析,php-fpm会把这些header代码解析为http header返回给nginx,nginx接收到php-fpm返回的数据,识别到有以上这些header,就会推送对应的文件到浏览器,当然这个header也是会发到浏览器的。
当然实际上写肯定不可能这样写,因为你也不太可能每个文件都写一遍,你可以用一些正则之类的方法判断用户请求的那个页面中需要哪些资源,然后用循环的方法去批量发送这些header。
这里要说一下php的header()
函数第二个参数要用false,表示的是允许同名的header title(默认是true,即前面有同名的会被后面的覆盖,但这不是我们想要的),如下就是在浏览器上的response header中看到的多个同名的header(字段名都是“link”):
当然你也可以直接拼好用一个header来发送:
<?php
header('link: </styles.css>; rel=preload; as=style, </script.js>; rel=preload; as=script, </example.png>; rel=preload; as=image, <https://fonts.example.com/font.woff2>; rel=preload; as=font; crossorigin; type="font/woff2"', false);
合起来在浏览器上看到的response header则是这样的:
如果是wordpress你就不用手动写了,直接有插件可以用:
方法二:通过html页面中的link标签
<link rel="preload" href="/styles.css" as="style">
<link rel="preload" href="/script.js" as="script">
<link rel="preload" href="/example.png" as="image">
<link rel="preload" href="/styles.css" as="style">
<link rel="preload" href="https://fonts.example.com/font.woff2" as="style" type="font/woff2" crossorigin="true">
最后一个crossorigin我也不确定是不是这么写的。
还可以用js来动态添加:
<script>
var res = document.createElement("link");
res.rel = "preload";
res.as = "document";
res.href = "/other/widget.html";
document.head.appendChild(res);
</script>
但这种添加link标签的方法我一直没有试成功,后面再看吧,W3C Preload中确实有说这种方法。
推荐文章:
HTTP2和HTTPS来不来了解一下?
HTTP/2之服务器推送(Server Push)最佳实践
Nginx 下实现 HTTP/2 服务器推送 (Server Push) 教程