Nginx HTTPS 性能优化
启于
一开始源于一个一闪而过的想法——反向代理是否会引入额外的延时?然后进一步思考:倘若 CDN 使用 https 回源,然后又用 https 反代,这光是握手是不是就得来回十几遍才行?
显然,放弃 https 不是一个合理的做法(事实上是一个疯狂的做法),那么就研究了一下 https 的优化思路,整合了一些网上的资源,在此记录一下。
以下配置并不适用于所有情况,最典型的,CDN 不提供某些配置选项,我们只能把我们掌控之下的事情做好。
一些思路
HTTP2
HTTP1.1 发布于1999年,已经是上个世纪的东西了。当时智能手机、自动驾驶还停留在科幻作品里。然而20年后的今年,那些曾经处于幻想中的设备,竟然还在使用当初的协议...
我们知道 TCP 的反复握手总是很恶心。一个网页往往有含有大量的资源,于是很多时间被浪费在建立连接上,可以到这个网站测试感受一下。对于 HTTP2,同域名只建立1个 TCP 连接,也就是说握手再也不会充斥我们的网路了。当然,还有其他优点,这里不再一一列举。
启用 HTTP2 很简单,加上一个标志即可:
listen 443 ssl; # 旧配置,改成下面这行
listen 443 ssl http2;
多数 CDN 应该支持此功能。
这个工具可以检测 HTTP2 以及下面几个配置项的状态。
启用 OCSP Stapling (装订)
对于个人站长,这个也许是作用最明显的了。由于一些不可描述的原因,Let's Encrypt 的证书验证服务器从中国大陆访问不是很稳定。通常客户端会按照 OSCP 协议去查询证书的状态(以免证书被吊销但本地的吊销列表没有及时更新),如果这一步骤很慢,则直接影响整体加载速度。
启用 OCSP Stapling 后,服务器会预先查询自己证书的状态,将带有 CA 签名的结果缓存在本地,与证书链一并发送给客户端,这样客户端不需要自己再进行请求,只要验证合法性即可。也就避免了网络因素导致的延时。
启用方法也不复杂,加上三行配置:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/full_chain.pem; # 完整证书链
多数 CDN 应该支持此功能。
启用 TLS 1.3
TLS 1.3 同样可以减少握手次数。对于旧版本,至少需要 2RTT(2个往返)才能完成 TLS 本身握手。如果访问刚才访问过的站点,通过会话恢复机制可以实现 1RTT 握手。
而 1.3 版本首次访问就仅需 1RTT,会话恢复则是 0RTT,没错,不需要单独往返握手了。
启用方法同样简单:
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; # 加上1.3版本就行
多数 CDN 应该支持此功能。
调整 ssl_buffer_size
故名思义,这就是一个缓冲大小,实际上可以理解为分包(TLS Record)大小。客户端必须收到一个完整的 Record 才能解密,Record 传输过程中又可能被分为多个 tcp 包。如果缓冲很大,万一中间丢包,则不得不重传,直到组成完整的 Record。如果比较小,则可以更及时地解密数据,相应地,分包个数也会增加,那么就不得不传输很多无意义的包头之类的数据。
默认大小是 16k,一般不用改。如果确认自己发送的数据都很小,比如一个 API 服务器,可以改得小一点,比如 4k。
ssl_buffer_size 4k;
SSL Session 缓存
若有缓存存在,下一次握手时不必进行完整的 TLS 交互,可以减少延时。尽管上面 HTTP2 已经减少了连接次数,但这不代表全程只需要1次连接,也不代表所有客户端都会支持 HTTP2,更不代表用户只会访问这一次,所以设置缓存还是有意义的。
## 设置缓存大小为 10m,1m 大约可以缓存 4000 个连接。
ssl_session_cache shared:SSL:10m;
## 缓存过期时间
ssl_session_timeout 1h;