HTTP/3 Alpn? 为什么网站开启了HTTP3浏览器却是用HTTP/2访问?

Nginx1.25 开始开始支持HTTP/3, 当我使用最新的Chrome(116.0.0.0)访问网站,并非每次都是用HTTP/3,很多次访问同一网站还是采用HTTP/2,就如下图,是5分钟内先后两次的访问记录。

这里就引出一个问题,客户端这里专指浏览器是怎么知道要访问的网站采用的HTTP1.1、HTTP/2还是HTTP/3?


一、ALPN(Application-Layer Protocol Negotiation)应用层协议

ALPN 是一个概念,目的是客户端服务端协商应用层协议,采用HTTP1.1还是HTTP/2当然也包含已经过期的SPDY。

1.为什么需要做协议协商?

道理很简单客户端访问服务器之前,并不知道服务器采用何种HTTP版本或者支持那些版本。必须通过一种方式来确定客户端到底采用那种方式传输数据。
这个问题产生于HTTP/2诞生,草案阶段有个名叫SPDY的协议,采用NPN方式。随着HTTP/2正式发布,最终主流采用了 TLS ALPN Application-Layer Protocol Negotiation Extension。无论何种方式目的只有一个:尽量少的通信情况下协商好客户端与服务器的通信方式。

2.协商协议的进化

  • NPN(Transport Layer Security (TLS) Next Protocol Negotiation Extension)

    这是一种HTTP/2发布前的SPDY协议使用的方案,这个方案与当前主流的TLS ALPN最相似,不同的是服务端端需要发送支持的协议列表到客户端。协议最终没有发布,只有4个草案。Next Protocol Extension

    spdy npn

  • The ALPN HTTP Header Field(HTTP 头字段协商)

    除了在TLS握手阶段做协议协商,还有一种采用HTTP头的ALPN。与TLS NPN/ALPN不同的是,这种方式依赖HTTPUpgrade完成后续的协议升级。而且网络中不能使用代理,道理也好理解,代理不能打开HTTP包检查头信息。这种方式也没有成为过主流。更多参照The ALPN HTTP Header Field, rfc7639。下面是个头协商的案例:

    http header demo
    CONNECT www.example.com HTTP/1.1
    Host: www.example.com
    ALPN: h2, http%2F1.1
  • Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension(TLS APLN Extension)

    http/2 alpn

    这是当前主流的协议协商方式,包括curl,chrome等主流客户端,nginx等主流服务器都采用这个方式。
    从下面这个trace信息,我们能看到客户端列出 h2 http1.1 交给服务器来选择,服务器最终采用server accepted to use h2 HTTP/2通信。相对于头协商不需要upgrade本次就能采用协商好的协议通信。

    http tls handshake demo
    * Trying 172.17.0.1:443...
    * TCP_NODELAY set
    * Connected to notes.icool.io (172.17.0.1) port 443 (#0)
    * ALPN, offering h2
    * ALPN, offering http/1.1
    * successfully set certificate verify locations:
    * CAfile: /etc/ssl/certs/ca-certificates.crt
    CApath: /etc/ssl/certs
    * TLSv1.3 (OUT), TLS handshake, Client hello (1):
    * TLSv1.3 (IN), TLS handshake, Server hello (2):
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    * TLSv1.2 (IN), TLS handshake, Server key exchange (12):
    * TLSv1.2 (IN), TLS handshake, Server finished (14):
    * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
    * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
    * TLSv1.2 (OUT), TLS handshake, Finished (20):
    * TLSv1.2 (IN), TLS handshake, Finished (20):
    * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
    * ALPN, server accepted to use h2
    * Server certificate:
    * subject: CN=notes.icool.io
    * start date: Aug 27 05:59:58 2023 GMT
    * expire date: Nov 25 05:59:57 2023 GMT
    * subjectAltName: host "notes.icool.io" matched cert's "notes.icool.io"
    * issuer: C=US; O=Let's Encrypt; CN=R3
    * SSL certificate verify ok.
    * Using HTTP2, server supports multi-use
    * Connection state changed (HTTP/2 confirmed)
    * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
    * Using Stream ID: 1 (easy handle 0x560ce197c270)
    > HEAD / HTTP/2
    > Host: notes.icool.io
    > user-agent: curl/7.68.0
    > accept: */*

二、HTTP/3是怎么协商协议的?

HTTP/3与HTTP/1.1、HTTP/2不同,h3采用quic协议传输层是UDP。协议协商与HTTP/2面对的问题更大复杂。因为采用TLS APLN Extension完全不能解决问题,这里的原因也不难理解。

* Trying 172.17.0.1:443...
* TCP_NODELAY set
* Connected to notes.icool.io (172.17.0.1) port 443 (#0)
* ALPN, offering h3
* ALPN, offering h2
* ALPN, offering http/1.1

假如协商过程是上面那样,就会产生一个问题:真这样握手完,然后呢?没有然后了,这个TCP连接没有用了,看起来浪费一次TCP连接,浪费一次TLS握手。

所以看起来我们已知的协商方式,看起来都不是完美方案。虽然都是不是完美方案,但依旧可以从上面的的方案选择一个看起来代价比较小的方案。HTTP 头字段协商,不同的是HTTP/3采用替代服务通告方式来通告客户端支持的协议,甚至可以可以指定不同的端口号。

1. HTTP Alternative Services(HTTP变更服务)

这是一个在HTTP标准库的头信息,具体格式如下

Alt-Svc: clear
Alt-Svc: <protocol-id>=<alt-authority>; ma=<max-age>
Alt-Svc: <protocol-id>=<alt-authority>; ma=<max-age>; persist=1
#这是www.google.com的头信息信息,可以看到这个支持http/3 和 http/3 草案29
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

当第一次收到 Alt-Svc信息后,浏览器会在一段时间内保留这个信息,下次再次访问的时候直接按照通告内容直接使用HTTP/3 访问了。

2. ALPN还能用吗?

HTTP/3 协议RFC9114 3.1实际上并没有限制ALPN,Alt-Svc并没有完全解决需要首先建立h2连接的问题,同样的ALPN也可以采用这个方式,只是主流浏览器不支持而已。

3. 有什么更好的方案吗?

是的,SVCB

SVCB是一个与SRV类似的DNS记录,用来标识支持的协议类型服务端口号等信息。但这个依旧处在开发阶段,
Cloudflare已经支持增加这个记录了,更多信息可以参考 https://blog.cloudflare.com/speeding-up-https-and-http-3-negotiation-with-dns/

#这是本站的SVCB解析记录
notes.icool.io. 300 SVCB 1 notes.icool.io. alpn=h3,h2

三、One more thing

1. 测试网站是否支持HTTP/3

https://http3check.net/

2. 测试浏览器是否支持HTTP/3

https://quic.nginx.org/


参考