既然上了HTTPS,就肯定得上HTTP2了,HTTP2的速度上升了不止一个档次,让网站飞起来吧。

如何升级到HTTP2

升级到HTTP/2其实很简单,我们只需要注意这几点:

  • HTTP2现在需要HTTPS
  • HTTP2要求Nginx版本是1.9.5以上
  • openssl版本要求1.0.2

然后,修改Nginx的配置:在listen 443 ssl 后面加上http2 default_server就行了

理论上,这样就可以了。

但是我配置之后通过浏览器请求头看到http的请求还是http1.1的,于是开启了我的HTTP2折腾之旅。


注:以下为开启失败后的折腾

什么是NPN和ALPN?

通过测试发现ALPN没开启。NPN开启了,但是ALPN没开启。

NPN,是一个 TLS 扩展,由 Google 在开发 SPDY 协议时提出。随着 SPDY 被 HTTP/2 取代,NPN 也被修订为 ALPN。NPN 是服务端发送所支持的 HTTP 协议列表,由客户端选择;而 ALPN 是客户端发送所支持的 HTTP 协议列表,由服务端选择;是否支持 NPN 或 ALPN 完全取决于使用的 OpenSSL 版本。在Chrome51之后必须ALPN访问http2。
理论上没有支持ALPN,我们还是可以通过其它浏览器支持http2,但是我发现现在还是不行的。为了排除某些不知道的因素,还是先看一下怎么支持ALPN。

是否支持ALPN

执行命令:
openssl s_client -alpn h2 -servername www.gzpblog.com -connect www.gzpblog.com:443 < /dev/null | grep 'ALPN'
显示:No ALPN negotiated
显然不支持。查了资料发现需要开启OCSP Stapling。

查看OCSP Stapling是否开启
执行命令:openssl s_client -connect www.gzpblog.com:443 -status -tlsextdebug < /dev/null 2>&1 | grep -i "OCSP response"
获得的信息是:OCSP response: no response sent
就是 OCSP Stapling 还没开启。接下来开启,我们需要获取证书 OCSP Response。

获取证书 OCSP Response

这部分参考自取屈大神的一篇:从无法开启 OCSP Stapling 说起,所以我们要做的就是准备好待验证网站证书链上的所有证书。证书链一般由根证书、一个或多个中间证书、站点证书组成。整理好这三个证书。

获取根证书

根证书因为SSL证书提供商的不同而不同,我的是Symantec的证书。
可以直接通过google浏览器F12->Security->View certificate看到根证书:

查到根证书是哪张了,可以通过火狐浏览器导出这张证书:

将导出的证书重命名为:root.pem

获取中间证书和站点证书

有两种情况吧:第一种如果你是通过阿里云等第三方帮你直接生成的证书,比如阿里云的免费证书,下载的证书中public.pem是站点证书,chain.pem是中间证书。第二种情况,就是不管怎么样,你就是找不到你的证书了,那,我们可以这样来弄:
通过命令:openssl s_client -connect www.gzpblog.com:443 -showcerts < /dev/null 2>&1

获取到的内容中 Certificate Chain 这一节,编号为 0 的证书是站点证书;编号为 1 的证书是中间证书。比如我的证书链一共是三级,服务端只需要发送两个证书;四级证书链服务端发送三个证书,根证书无需发送。
将站点证书保存为 site.pem;中间证书保存为 intermediate.pem(如果有多个中间证书,按照子证书在上的顺序保存)
验证一下每个证书的 Common Name:

# openssl x509 -in /usr/local/nginx/cert/site.pem -noout -subject
subject= /CN=www.gzpblog.com
# openssl x509 -in /usr/local/nginx/cert/intermediate.pem -noout -subject
subject= /C=US/O=Symantec Corporation/OU=Symantec Trust Network/OU=Domain Validated SSL/CN=Symantec Basic DV SSL CA - G1
# openssl x509 -in /usr/local/nginx/cert/root.pem -noout -subject
subject= /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5

获取站点证书的 OCSP 服务地址:

# openssl x509 -in /usr/local/nginx/cert/site.pem -noout -ocsp_uri
http://hc.symcd.com

使用以下命令获得站点证书的 OCSP Response:

openssl ocsp -issuer /usr/local/nginx/cert/intermediate.pem -cert /usr/local/nginx/cert/site.pem -no_nonce -text -url http://hc.symcd.com


可以看到site.pem:good;说明证书合法。出现一个Response Verify Failure,这是因为我们没有告诉 openssl应该信任哪些证书,openssl 无法验证 OCSP Response 内容而报的错。这个错误可以通过加上 -noverify 参数屏蔽,但更好的做法是通过 -CAfile 指定信任证书,我们可以这样做:
将根证书、全部中间证书按照子证书在上的顺序,保存为 chain.pem。再次执行:

openssl ocsp -CAfile /usr/local/nginx/cert/chain.pem -issuer /usr/local/nginx/cert/intermediate.pem -cert /usr/local/nginx/cert/site.pem -no_nonce -text -url http://hc.symcd.com


Verify OK!搞定了。

配置Nginx

Nginx中加上配置:

ssl_stapling               on;
ssl_stapling_verify        on;
ssl_trusted_certificate    /usr/local/nginx/cert/chain.pem;

重启Nginx
再次使用以下命令查看OCSP Stapling是否开启

openssl s_client -connect www.gzpblog.com:443 -status -tlsextdebug < /dev/null 2>&1 | grep -i "OCSP response"

这时可以看到;

OCSP response:
OCSP Response Data:
OCSP Response Status: successful (0x0)
Response Type: Basic OCSP Response

说明我们已经成功获取了OCSP Response,OSCP Stapling开启成功。

如果出现:verify error:num=20:unable to get local issuer certificate
以上的命令,chain.pem等证书部分我都加了路径,这主要是当时不是自己生成证书,openssl的工作路径什么的没有设置。所以我会报错verify error:num=20:unable to get local issuer certificate,这个错误没什么问题,你也可以在命令中加上你的证书: -CAfile /usr/local/nginx/cert/chain.pem

重新编译Nginx

按理来说,开启了nginx中的配置中的http2就能生效了。但是我的一直不生效,而且ALPN也没有开启。一直纠结在这里,因为我编译没有设定openssl路径,服务器装的openssl版本是1.1.0的;Nginx版本升级到了1.11.10;然后还是不行。

最后我发现,问题在于,我虽然没有配置openssl源码路径编译nginx,但是我连–with-openssl都没写,这就导致直接不支持openssl了!犯浑了,于是乎我重新编译了nginx,而且指定了源码进行编译。

惊讶的发现还是不行。最后发现了原因,是我操作linux系统时的失误,也让我学到一点:

之前编译过的nginx,加了service的,通过:

service nignx reload

重新启动,新版本不会生效,php-fpm一直监听老版本;然后我通过:

/usr/sbin/nginx stop

/usr/sbin/nginx

然后php-fpm才监听到新版本,之后service nignx reload 就正常了。这个机制我还不太理解是为什么,不过这样操作才可以。

现在确定Nginx是正常的版本之后,重新查看是否支持ALPN,可以看到已经支持h2:

同时网站已经支持http2,确定一开始不行就是因为Nginx没编译好。通过浏览器的请求头我们已经可以看到HTTP/2.0的请求:

同时再看一下google是否已经支持:
可以通过HTTP/2 and SPDY indicator(科学上网获取)这个拓展插件方便查看是否已经启用http2,右上角蓝色闪电标志,说明已启用;或者直接打开chrome://net-internals/#http2查看:

完成

HTTP/2升级完毕。其实就是各软件版本要对得上,Nginx编译正确就可以。在完成HTTP/2的升级之后,发现在 SSL Labs 上的评分也由B变成了A+: