一开始源于一个一闪而过的想法——反向代理是否会引入额外的延时?然后进一步思考:倘若 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;

参考

Last modification:September 6, 2021