好了,近期最后一篇关于 HTTPS 的文章,紧接前面两篇,HTTP/2 的生效在当下要以启用 TLS 的 HTTPS 为前提,我会力求详尽地分享这个过程中学到的东西。

HTTP/2 是什么

HTTP/2 是下一代的超文本传输协议,用于在未来替代当下主流的 HTTP/1.1。

HTTP/2 协议制定动机其实就一个 —— 进一步压榨 TCP 连接性能,加快资源传输效率。

其效果在多资源请求数下尤为明显,由“万维网之父”中的 Tim Berners-Lee 创立的 Akamai 公司进行过一项对比测试,很直观地展现了 HTTP/1 和 HTTP/2 的速度差异:测试地址

启用次世代协议 HTTP/2,少年确定不来一发吗-极客飞船

网页中的地球其实是张由 300 多张小图拼接而成的,使用 F12 开发者工具就可以验证这一点,

这个平台测试的结果变动比较大,不过通常 HTTP/2 的成绩都比 HTTP/1 快几倍。

为什么要顶替 HTTP/1.1

在 HTTP/1 时代,人们想了有很多优化 web 性能办法,比如把多张小图整合成一张大图,然后利用 JS 或 CSS 定位、裁剪(Spriting);比如把资源从不同的域名不同的主机上加载(Sharding)。

其实这些工作都可以归结到一个起因 —— 在 HTTP/1 下,一个资源请求/响应就要占用一个 TCP 连接,而目前浏览器同一时间对单位主机建立的 TCP 连接上限,通常只有 6 ~ 8 个。

有上限,就有队列,队列越长,堵塞的可能就越大,所以才有了上面那些“曲线救国”的办法。

同时在一些其他方面,HTTP/1 依然让人头疼,像头部信息中经常重复的内容,如 cookies 、 user-agent ,HTTP/1 都不会做任何压缩,页面资源越多,头部信息不必要的资源占用也就越大。

HTTP/2 的特性

多路复用,单位主机的资源请求/响应只在一个 TCP 连接中进行,这个 TCP 连接中可以打开多个流(Stream)进行双向传输;

ServerPush,客户端在请求资源 A 后服务端主动推送相关资源 B/C/D;

头部压缩(HPACK),压缩头部信息中的重复内容,降低服务端资源消耗;

重置,当终止带有明确 content-length 的消息发送后,可以通过 RST_STREAM 帧实现重新发送这个消息,而不用再从头再握手建立 TCP 连接。

其实还有更多的特性,但惭愧才疏学浅,读完 HTTP/2 标准化一线工作者 Daniel Stenberg 的文档后,能说得上明白的就这些。这份文档还讲了 HTTP/2 的前世今生,译文和原版能在这里找到:传送门

重要的是,在 HTTP/2 下,很多以往针对 HTTP/1.1 的优化工作就不用再实施了。

比如把资源分散到多个域名对应的主机上的 Sharding ,这种方案反而会影响 HTTP/2 的性能。在部署速度和管理效率上,HTTP/2 更有优势,同时还节省了额外购买域名的成本。

启用 HTTP/2 的前提

HTTP/2 协议本身并没有要求必须基于 TLS,但当下主流的浏览器都仅支持基于 TLS 的 HTTP/2 。

通过这个站点,可以查询到当前支持 HTTP/2 的浏览器:传送门

启用次世代协议 HTTP/2,少年确定不来一发吗-极客飞船

简而言之 —— 启用 HTTP/2 先要启用 HTTPS,关于 HTTPS 的启用和部署细节,之前已经写了两篇文章 ——「开启全站 HTTPS(附百度分享本地化)」、「把 HTTPS 安全评级提升到 A+

在找 HTTPS 配置攻略的朋友,瞧一瞧看一看咯,吃不了亏上不了当 : P

此外,HTTP/2 需要依托于较新的 Webserver ,比如常见的 Apache 需要 2.4.17+,Nginx 1.9.5 +。

其他的 Webserver 我没有用过,这里提供一个列表,可以查阅很多 Webserver 对 HTTP/2 的支持情况:传送门

平滑升级 Nginx

Nginx 最近的每一次迭代,几乎都有对 HTTP/2 进行修复和优化,当前最新的稳定版是 nginx-1.12.0 ,对 1.11.x 主线版进行了特性合并和 DEBUG ,建议升级到该版本。

下面是我的升级流程:

# 先查询并记录 nginx 原来的参数,"V"必须大写
nginx -V

# 获取资源
wget http://nginx.org/download/nginx-1.12.0.tar.gz

# 解压
tar xzvf nginx-1.12.0.tar.gz

# 进入目录
cd nginx-1.12.0

# 配置原本的参数
# 各人的参数可能不同,我的仅做演示
# 关键是 HTTP/2 模块,如果原来没有,现在需要加入 --with-http_v2_module
./configure --user=www --group=www --prefix=/server/nginx --with-openssl=../openssl-1.0.2k --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-stream --with-stream_ssl_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E

# 编译
make

# 移走并重命名旧版本的 nginx 二进制文件
mv /server/nginx/sbin/nginx /server/nginx/backup/nginx.old

# 复制刚编译的新版本 nginx 二进制文件到原来目录
cp objs/nginx /www/server/nginx/sbin

# 执行升级
make upgrade

# 重启 nginx
service nginx restart

再用命令 nginx -V 查询,Nginx 已经是最新的 1.12.0 了。

启用次世代协议 HTTP/2,少年确定不来一发吗-极客飞船

开启 HTTP/2 和 Nginx 完整配置

环境支持就位后,开关 HTTP/2 就很简单了,只需要在原本的 Nginx 443 端口配置中加上一句「http2 」,例如:listen 443 ssl http2 ,由于 HTTP/2 对 CipherSuite 的要求更加严格,ssl_ciphers 配置项稍有不对就可能导致某些环境下无法访问。

我用了一种偏保守的策略,直接过滤掉不安全的加密套件,经过 ssllabs.com 检测,除了比较远古的环境,其他都是 TLS :

server
{
listen 80;
return 301 https://geekufo.com$request_uri;
}
server
{
listen 443 ssl http2 fastopen=3 reuseport;
server_name geekufo.com www.geekufo.com;
index index.php;
root /网站目录;

# 配置证书路径
ssl_certificate /公钥路径;
ssl_certificate_key /私钥路径;

# 加入2048位的迪菲、赫尔曼算法秘钥
# 要先手动生成
# 下面是生成命令,路径可以自定义
# openssl dhparam -out /www/ssl/dhparam.pem 2048
ssl_dhparam /www/ssl/dhparam.pem;

# 允许的协议,针对降级攻击,禁用SSLv2 SSLv3
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

# 优先使用服务端设定的加密套件,而不是浏览器的
ssl_prefer_server_ciphers on;

# 禁用不安全的加密套件
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4';

# 优化TLS握手,缓存链接凭据
ssl_session_timeout 1h;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets on;

# OCSP封套
ssl_stapling on;
ssl_stapling_verify on;
resolver 114.114.114.114 valid=300s;
resolver_timeout 10s;

#后面省略了关系不大的配置
......
}

测试方法

列几个检查 HTTP/2 有没有启用的方法,各凭喜好选择吧。

一个可以检测 HTTP/2 的在线平台 :传送门

启用次世代协议 HTTP/2,少年确定不来一发吗-极客飞船

Firefox 浏览器自带的 F12 开发者工具也能查看。

启用次世代协议 HTTP/2,少年确定不来一发吗-极客飞船

Chrome 浏览器的话,直接输入 chrome://net-internals/#http2 ,就会列出标签页里的 HTTP/2 站点。

也可以直接安装 HTTP/2 检测插件 HTTP/2 and SPDY indicator(传送门),Chrome 启用后,凡是访问 HTTP/2 站点,右上角都会显示 启用次世代协议 HTTP/2,少年确定不来一发吗-极客飞船 标志。

小结

最后说说 HTTP/2 的收益。

讲真,启用 HTTP/2 之后,我这个小站的加载速度并非预想的那样,有什么惊艳进步。

感官上,的确快上了一点点,仅仅是一点点。

从数据来说,当前用 Chrome 浏览器“强刷”首页,Load 时间通常在 860ms 左右,清除所有缓存后重新打开,Load 时间通常在 1.3s 左右,而启用 HTTP/2 之前,两项成绩大约在 950ms 和 1.5s 。

总结,如果是非常想专攻 HTTP/2 来改善网站加载速度,我觉得优先其他方法,像减小、转移、删除不必要的资源负载,建设完整的缓存体系等,成效更明显。

文末再安利一个图片格式 —— webp ,一张 13M 的 jpg 图片经实测可以转换成只有 1.24M 的 webp,而画质几乎不变。

如果不介意多出转换格式这一工作,可谓“减负”神器。