在AWS NLB后面的ECS上运行的Spring Boot应用中,HTTP/2请求因PROTOCOL_ERROR失败(HTTP/1.1可以正常工作)

后端开发 2026-07-08

在将应用程序升级到Spring Boot 4.1.0之后,一旦服务在配置了TLS监听器的AWS Network Load Balancer (NLB) 后端运行,请求就开始失败。相同的构建在升级前能够工作。

我们的部署在NLB通过TLS监听器进行TLS终止(offload),从NLB到目标组的连接是明文、非TLS的。应用程序运行在目标组后面的AWS Fargate上。

令人费解的部分在于失败仅出现在HTTP/2上。通过HTTP/2的请求在协议层被拒绝(流被重置),而同样的请求在HTTP/1.1下能够正常工作。

可用(HTTP/1.1):

❯ curl --http1.1 http://example.com/actuator/health
{"groups":["liveness","readiness"],"status":"UP"}

损坏(HTTP/2):

❯ curl --http2 --http2-prior-knowledge http://example.com/actuator/health
curl: (92) HTTP/2 stream 1 reset by server (error 0x1 PROTOCOL_ERROR)

我们的应用代码并没有以任何应该影响此问题的方式发生变化。把Spring Boot的日志级别一路提高到 TRACE,遗憾的是没有揭示出任何真正的线索。

解决方案

在这里自问自答,以便记录在某处。希望能帮助到某些人。我们将问题追溯到与Spring Boot 4.1.0一同发布的Tomcat版本中的一个新校验。

根本原因

在NLB进行TLS终止的情况下,客户端通过HTTPS连接,但从NLB到后端的连接是明文(非TLS)。当使用HTTP/2时,原始的 :scheme 虚拟头字段(https)仍会被转发到后端。HTTP/1.1没有这样的虚拟头字段,这也是为什么HTTP/1.1的流量没有受到影响的原因。

在四月,Tomcat增加了一个检查:HTTP/2的 :scheme 虚拟头字段是否与连接器实际是否启用TLS相一致。相关提交为 apache/tomcat@81712d2。如果传入的方案是 https,但连接器未启用SSL(我们的情形,因为后端连接是明文),Tomcat会以 PROTOCOL_ERROR / RST拒绝该流。

该提交作为Spring Boot 4.1.0的一部分在6 月10日发布,因此在我们升级的瞬间问题就显现出来了。

我们如何发现

我们对NLB与容器之间的流量拍了一份 tcpdump。在转发的 :scheme 上看到对一个明显非TLS跳点的 https,这正是把我们指向一致性检查的证据。

随后,我们可以在本地复现实验:通过在纯明文连接上发送带有 :schemehttps 的HTTP/2请求,结果同样失败:

❯ curl --http2 --http2-prior-knowledge http://localhost:8080/actuator/health -H ':scheme: https'
curl: (92) HTTP/2 stream 1 reset by server (error 0x1 PROTOCOL_ERROR)

解决方案

也在容器上使用TLS

让到容器的网络路径实际使用TLS,使转发的 :scheme: https 与传输层保持一致。这意味着将NLB目标组配置为使用TLS(重新加密),并在容器上提供TLS支持。

顺便说一句:容器上的证书可以是自签名的。根据我们的经验,NLB会重新加密到目标主机,而不要求目标证书链到一个受信任的CA。[来自AWS的文档]:

如果目标组配置了TLS协议,负载均衡器将使用你在目标上安装的证书与目标建立TLS连接。负载均衡器不会验证这些证书。因此,你可以使用自签名证书或已过期的证书。由于负载均衡器位于一个虚拟私有云(VPC)中,负载均衡器与目标之间的流量在数据包层面进行身份验证,因此即使目标上的证书无效,也不易成为中间人攻击或伪造攻击的风险。

退出对该检查

事后我们才发现,这个新“特性”在Tomcat中被标记为一个bug:Bug 70091 - HTTP/2 :scheme 验证在10.1.55中对来自TLS终止反向代理的RFC合规翻译请求拒绝;需要一个退出机制。解决办法是添加一种退出此行为的方式:AllowSchemeMismatch。如此处所述

可以用来启用或禁用在HTTP/2流中用户代理所呈现的方案与接收该流的通道之间的一致性检查的布尔值。应仅在使用受信任的TLS终止反向代理的情况下将其设为true。如未指定,该属性将设为false。

退出机制在Tomcat中落地

  • 11.0.23及以上
  • 10.1.56及以上
  • 9.0.119及以上
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章