什么是 ALPN

ALPN(Application-Layer Protocol Negotiation,应用层协议协商)是 TLS 协议的一个扩展,定义于 RFC 7301。它允许客户端和服务器在 TLS 握手阶段就协商好使用哪个应用层协议,而无需额外的往返通信。

如果没有 ALPN,客户端只能先建立 TLS 连接,再通过 NPN(Next Protocol Negotiation,已废弃)或在 TLS 握手完成后通过额外协商来确定使用 HTTP/1.1 还是 HTTP/2。ALPN 将这个过程整合到 TLS 握手中,减少了延迟。

ALPN 的工作原理

在 TLS ClientHello 消息中,客户端通过 application_layer_protocol_negotiation 扩展发送自己支持的协议列表,按优先级排序。服务器从中选择一个双方都支持的协议,并在 ServerHello 中回显该选择。

整个协商过程在 TLS 握手内部完成,对应用层透明:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Client                          Server
  |                                |
  |-------- ClientHello ---------->|
  |  + ALPN: [h2, http/1.1]       |
  |                                |
  |<------- ServerHello -----------|
  |  + ALPN: h2                   |
  |                                |
  |-------- 后续 TLS 握手 -------->|
  |                                |
  |<===== TLS 加密通道建立 ========>|
  |      使用 HTTP/2 通信          |

常见的 ALPN 协议标识符

协议标识符 说明
h2 HTTP/2 over TLS
http/1.1 HTTP/1.1
http/1.0 HTTP/1.0(极少使用)
h3 HTTP/3 over QUIC(不经过 TLS,使用 QUIC 自带协商)
dot DNS over TLS
acme-tls/1 ACME TLS-ALPN-01 验证方式

协议标识符的格式由 RFC 7301 Section 3.1 定义,是长度前缀的字节串。

使用 OpenSSL 测试 ALPN

基本测试

使用 openssl s_client-alpn 参数可以指定客户端请求的协议列表(逗号分隔):

1
2
# 请求优先使用 HTTP/2,回退到 HTTP/1.1
echo | openssl s_client -connect www.cloudflare.com:443 -alpn h2,http/1.1 2>/dev/null | grep "ALPN protocol"

实际输出:

1
ALPN protocol: h2

Cloudflare 服务器支持 HTTP/2,因此协商结果为 h2

测试不支持 H2 的服务器

1
2
# 百度仅支持 HTTP/1.1
echo | openssl s_client -connect www.baidu.com:443 -alpn h2,http/1.1 2>/dev/null | grep "ALPN protocol"

实际输出:

1
ALPN protocol: http/1.1

即使客户端请求了 h2,服务器不支持时会自动回退到 http/1.1

仅请求 H2 时的表现

当客户端只请求 h2,而服务器不支持时:

1
echo | openssl s_client -connect www.baidu.com:443 -alpn h2 2>/dev/null | grep "ALPN"

实际输出:

1
No ALPN negotiated

这种情况下,服务器没有任何匹配协议,ALPN 协商失败,连接可能使用默认协议或断开。

不指定 ALPN 参数

1
2
# 不发送 -alpn 参数
echo | openssl s_client -connect www.baidu.com:443 2>/dev/null | grep "ALPN"

实际输出:

1
No ALPN negotiated

不使用 -alpn 参数时,客户端不会发送 ALPN 扩展,因此不会进行协议协商。

使用 -tlsextdebug 查看扩展详情

添加 -tlsextdebug 参数可以查看 TLS 扩展的详细信息:

1
echo | openssl s_client -connect www.cloudflare.com:443 -alpn h2,http/1.1 -tlsextdebug 2>&1 | grep -i "alpn"

输出:

1
ALPN protocol: h2

Nginx 中的 ALPN 配置

在 Nginx 中启用 ALPN 非常简单,只需在 listen 指令中添加 http2 参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # ALPN 会自动协商 h2 和 http/1.1
    # 客户端请求 [h2, http/1.1] 时,Nginx 优先选择 h2
}

当 Nginx 配置了 http2 后,它会在 TLS ServerHello 中发送 ALPN 扩展,告知客户端支持 h2http/1.1

验证 Nginx 配置

配置完成后,可以使用 OpenSSL 验证:

1
echo | openssl s_client -connect example.com:443 -alpn h2 2>&1 | grep "ALPN protocol"

期望输出:

1
ALPN protocol: h2

ALPN 与 NPN 的区别

NPN(Next Protocol Negotiation)是 Google 在 SPDY 协议中引入的早期方案,后被 ALPN 取代。两者的关键区别:

特性 NPN ALPN
协商顺序 服务器发送协议列表,客户端选择 客户端发送协议列表,服务器选择
标准化 Google 草案 RFC 7301(IETF 标准)
安全性 较低(客户端决定协议) 较高(服务器决定协议)
当前状态 已废弃 广泛使用

ALPN 将协议选择权交给服务器,这更安全——服务器可以根据自己的负载情况和策略决定使用哪个协议。

ALPN 的实际应用场景

HTTP/2 启用

这是 ALPN 最常见的用途。浏览器在 TLS 握手时发送 h2 请求,服务器支持则协商使用 HTTP/2,否则回退到 HTTP/1.1。整个过程对用户完全透明。

HTTP/3 的特别说明

HTTP/3 运行在 QUIC 协议之上,不经过传统 TLS 层。QUIC 有自己的应用层协议协商机制(在 CRYPTO frame 中交换),因此不使用 ALPN 扩展。但概念是相似的——在加密握手阶段完成协议协商。

ACME TLS-ALPN-01 验证

Let’s Encrypt 等 CA 机构提供 TLS-ALPN-01 验证方式。在证书申请过程中,ACME 服务器通过 ALPN 协商 acme-tls/1 协议来验证域名所有权:

1
2
# 模拟 ACME TLS-ALPN 验证请求(仅供理解原理)
echo | openssl s_client -connect example.com:443 -alpn acme-tls/1 2>/dev/null | grep "ALPN"

DNS over TLS

DNS over TLS(RFC 7858)使用 ALPN 协议标识符 dot 来协商加密 DNS 连接:

1
2
# Cloudflare DNS over TLS
echo | openssl s_client -connect cloudflare-dns.com:853 -alpn dot 2>/dev/null | grep "ALPN"

排查 ALPN 相关问题

问题:HTTP/2 未生效

排查步骤:

  1. 确认 Nginx 配置了 http2

    1
    
    grep -r "http2" /etc/nginx/conf.d/
    
  2. 检查 OpenSSL 版本:ALPN 需要 OpenSSL 1.0.2+:

    1
    
    openssl version
    
  3. 验证 ALPN 协商结果

    1
    
    echo | openssl s_client -connect yourdomain.com:443 -alpn h2 2>&1 | grep "ALPN"
    

    如果输出 No ALPN negotiated,说明服务器未正确配置 HTTP/2。

问题:浏览器显示 HTTP/1.1

使用浏览器开发者工具查看协议类型,或直接从命令行测试:

1
2
# 使用 curl 查看协议版本
curl -sI --http2 https://yourdomain.com/ 2>&1 | head -1

如果返回 HTTP/1.1 200 OK,说明 ALPN 协商未成功选择 HTTP/2。

小结

ALPN 是现代 TLS 通信中不可或缺的一环:

  • 将应用层协议协商整合到 TLS 握手中,减少额外往返
  • 服务器掌握协议选择权,比 NPN 更安全
  • 是 HTTP/2 在 HTTPS 上广泛使用的基础
  • 除 HTTP/2 外,还用于 DNS over TLS、ACME 验证等场景

理解 ALPN 有助于排查 HTTPS/HTTP2 相关问题,也是深入理解 TLS 握手过程的重要一环。

参考来源