为何Node.js中HTTPS上运行HTTP2正常,HTTP上运行却失败?
为什么Node.js中HTTP2基于HTTPS正常运行,基于HTTP却失败?
核心原因在于HTTP2的两种部署模式差异,以及客户端(curl)与Node.js服务器的交互逻辑:
1. HTTP2的两种运行模式
HTTP2有两种标准部署方式:
- 加密模式(h2):通过TLS握手时的ALPN协议协商,客户端与服务器自动切换到HTTP2。这是当前主流部署方式,现代浏览器和curl访问HTTPS时会默认尝试协商该模式。
- 明文模式(h2c):不经过TLS加密,直接在TCP上运行HTTP2。这种模式需要客户端主动触发协议切换:要么通过HTTP/1.1的
Upgrade请求升级到h2c,要么直接使用"先验知识"(prior knowledge)以h2c协议连接。
2. HTTPS版本正常的原因
你的HTTPS代码使用http2.createSecureServer创建加密HTTP2服务器,同时开启allowHTTP1: true:
- curl访问
https://localhost:443时,会自动通过TLS的ALPN协商请求使用HTTP2,服务器识别后建立HTTP2连接,正常返回结果。 - 即使协商失败,
allowHTTP1: true也允许服务器回退到HTTP/1.1处理请求。
3. HTTP版本失败的原因
你的HTTP代码使用http2.createServer创建明文h2c服务器,但curl默认不会对HTTP端口自动使用HTTP2:
- 直接执行
curl http://localhost:80时,curl会以HTTP/1.1协议发送请求,且不会自动发送Upgrade: h2c头触发协议升级。 - Node.js的h2c服务器收到纯HTTP/1.1请求且未触发升级时,无法正确解析请求,返回不符合HTTP/1.1规范的响应,导致curl报错
Received HTTP/0.9 when not allowed。
解决方法
要让curl正确访问明文h2c服务器,需显式指定HTTP2协议:
- 使用"先验知识"模式,直接告知curl服务器为h2c:
curl --http2-prior-knowledge http://localhost:80 - 或通过
--http2参数让curl尝试通过Upgrade头升级到h2c:curl --http2 http://localhost:80
执行以上命令后,即可正常获取Hello World!响应。
补充说明
- Node.js的
allowHTTP1: true在明文h2c服务器中确实支持处理HTTP/1.1请求,但curl的默认行为未触发正确的协议交互,导致报错。 - 明文h2c模式通常仅用于内部服务或特定场景,互联网环境几乎不会使用,因缺少加密安全性。
内容的提问来源于stack exchange,提问作者Nir O.
相关产品推荐
相关产品推荐

