Spring Cloud Gateway HTTPS配置中的NotSslRecordException深度解析与实战解决方案当开发者在Spring Cloud Gateway中配置HTTPS后经常会遇到一个令人困惑的错误NotSslRecordException。这个异常通常发生在网关将加密的HTTPS请求直接转发给下游微服务时而微服务期望接收的是普通的HTTP请求。本文将深入分析这一问题的根源并提供多种经过实战验证的解决方案。1. 问题现象与根源分析在实际生产环境中当开发者为Spring Cloud Gateway配置HTTPS后可能会在日志中看到如下错误堆栈io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1178) at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1243) ...这个错误的本质原因是客户端通过HTTPS访问网关网关正确解密了请求但在转发给下游微服务时仍然保持了https协议头而下游微服务并没有配置SSL/TLS导致无法处理加密的请求。关键问题点网关接收的是HTTPS请求加密网关转发时保留了httpsscheme下游微服务期望接收HTTP请求未加密Netty尝试将普通HTTP请求当作SSL/TLS记录解析2. 核心解决方案对比针对这一问题主要有两种主流解决方案各有优缺点解决方案实现方式优点缺点适用场景URI显式指定HTTP在路由配置中明确使用lb:http://配置简单一目了然需要修改所有路由配置新项目或路由较少的情况过滤器修改Scheme使用自定义过滤器修改请求scheme无需修改路由配置集中管理需要编写额外代码已有大型项目迁移3. 方案一路由URI显式指定HTTP协议这是最直接简单的解决方案只需在路由配置中明确指定使用http协议spring: cloud: gateway: routes: - id: service-route uri: lb:http://service-name # 关键在此处明确使用http predicates: - Path/api/**实现原理lb:http://前缀明确告知网关使用HTTP协议转发LoadBalancerClientFilter会根据此scheme创建请求下游微服务接收普通HTTP请求避免SSL解析错误注意事项确保所有路由配置都使用lb:http://前缀如果微服务本身也配置了HTTPS则需要保持lb:https://在服务发现场景下服务名(service-name)需与注册中心一致4. 方案二自定义过滤器修改Scheme对于已有大型项目修改所有路由配置可能不现实。这时可以通过自定义过滤器统一修改请求schemepublic class HttpSchemeFilter implements GlobalFilter, Ordered { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI originalUri exchange.getRequest().getURI(); ServerHttpRequest request exchange.getRequest(); // 只修改lb://开头的服务调用 if(originalUri ! null originalUri.getScheme().equals(lb)) { URI newUri UriComponentsBuilder.fromUri(originalUri) .scheme(http) // 强制修改为http .build() .toUri(); ServerHttpRequest newRequest request.mutate() .uri(newUri) .build(); return chain.filter(exchange.mutate().request(newRequest).build()); } return chain.filter(exchange); } Override public int getOrder() { return LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER - 1; } }注册过滤器Configuration public class GatewayConfig { Bean public HttpSchemeFilter httpSchemeFilter() { return new HttpSchemeFilter(); } }技术要点过滤器需要在LoadBalancerClientFilter之前执行只修改lb://开头的服务调用URI保持原始请求的其他部分不变可以通过配置中心动态控制是否启用5. 进阶混合HTTPS/HTTP环境配置在某些复杂场景中可能需要网关同时支持对外HTTPS访问对内HTTP服务调用部分特殊服务需要HTTPS调用这种混合环境的配置示例如下server: port: 8443 ssl: enabled: true key-store: classpath:keystore.p12 key-store-password: changeit key-store-type: PKCS12 spring: cloud: gateway: routes: - id: http-service uri: lb:http://http-service predicates: - Path/http-api/** - id: https-service uri: lb:https://https-service predicates: - Path/https-api/** - id: default-route uri: lb:http://default-service predicates: - Path/**关键配置项server.ssl.enabled: 启用网关HTTPS按服务需求分别配置lb:http://和lb:https://默认路由建议使用HTTP除非明确知道服务支持HTTPS6. 排查技巧与常见误区即使按照上述方案配置仍可能遇到问题。以下是几个排查技巧检查清单确认网关确实接收的是HTTPS请求curl -vk https://gateway:8443/api/endpoint检查路由配置中的URI scheme# 错误示例 uri: lb://service-name # 正确示例 uri: lb:http://service-name查看网关日志中的实际转发URLlogging.level.org.springframework.cloud.gatewayDEBUG确认下游服务确实运行在HTTP端口常见误区认为网关配置HTTPS后所有通信都会自动加密实际上网关到服务的通信需要单独配置忽略服务发现场景下的scheme继承问题在Kubernetes环境中混淆Service类型ClusterIP vs NodePort忘记在测试环境关闭证书验证开发阶段可配置insecure-skip-verify7. 性能考量与最佳实践在实施HTTPS网关方案时还需考虑性能影响性能优化建议使用现代加密算法TLS 1.3server: ssl: protocol: TLSv1.3 ciphers: TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256启用OCSP Stapling减少证书验证开销考虑硬件SSL加速如AWS ALB监控网关的SSL握手指标# Netty原生指标 jcmd pid VM.native_memory | grep Ssl架构建议在网关层集中管理SSL/TLS终止内部服务间通信保持HTTP简化架构对特别敏感的服务可启用双向TLS认证定期轮换证书并自动化部署流程通过以上方案和最佳实践开发者可以彻底解决Spring Cloud Gateway中的NotSslRecordException问题同时构建出既安全又高性能的微服务通信架构。
Spring Cloud Gateway配置HTTPS后,微服务调用报错NotSslRecordException?一个配置项帮你搞定
发布时间:2026/5/22 5:38:54
Spring Cloud Gateway HTTPS配置中的NotSslRecordException深度解析与实战解决方案当开发者在Spring Cloud Gateway中配置HTTPS后经常会遇到一个令人困惑的错误NotSslRecordException。这个异常通常发生在网关将加密的HTTPS请求直接转发给下游微服务时而微服务期望接收的是普通的HTTP请求。本文将深入分析这一问题的根源并提供多种经过实战验证的解决方案。1. 问题现象与根源分析在实际生产环境中当开发者为Spring Cloud Gateway配置HTTPS后可能会在日志中看到如下错误堆栈io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1178) at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1243) ...这个错误的本质原因是客户端通过HTTPS访问网关网关正确解密了请求但在转发给下游微服务时仍然保持了https协议头而下游微服务并没有配置SSL/TLS导致无法处理加密的请求。关键问题点网关接收的是HTTPS请求加密网关转发时保留了httpsscheme下游微服务期望接收HTTP请求未加密Netty尝试将普通HTTP请求当作SSL/TLS记录解析2. 核心解决方案对比针对这一问题主要有两种主流解决方案各有优缺点解决方案实现方式优点缺点适用场景URI显式指定HTTP在路由配置中明确使用lb:http://配置简单一目了然需要修改所有路由配置新项目或路由较少的情况过滤器修改Scheme使用自定义过滤器修改请求scheme无需修改路由配置集中管理需要编写额外代码已有大型项目迁移3. 方案一路由URI显式指定HTTP协议这是最直接简单的解决方案只需在路由配置中明确指定使用http协议spring: cloud: gateway: routes: - id: service-route uri: lb:http://service-name # 关键在此处明确使用http predicates: - Path/api/**实现原理lb:http://前缀明确告知网关使用HTTP协议转发LoadBalancerClientFilter会根据此scheme创建请求下游微服务接收普通HTTP请求避免SSL解析错误注意事项确保所有路由配置都使用lb:http://前缀如果微服务本身也配置了HTTPS则需要保持lb:https://在服务发现场景下服务名(service-name)需与注册中心一致4. 方案二自定义过滤器修改Scheme对于已有大型项目修改所有路由配置可能不现实。这时可以通过自定义过滤器统一修改请求schemepublic class HttpSchemeFilter implements GlobalFilter, Ordered { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI originalUri exchange.getRequest().getURI(); ServerHttpRequest request exchange.getRequest(); // 只修改lb://开头的服务调用 if(originalUri ! null originalUri.getScheme().equals(lb)) { URI newUri UriComponentsBuilder.fromUri(originalUri) .scheme(http) // 强制修改为http .build() .toUri(); ServerHttpRequest newRequest request.mutate() .uri(newUri) .build(); return chain.filter(exchange.mutate().request(newRequest).build()); } return chain.filter(exchange); } Override public int getOrder() { return LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER - 1; } }注册过滤器Configuration public class GatewayConfig { Bean public HttpSchemeFilter httpSchemeFilter() { return new HttpSchemeFilter(); } }技术要点过滤器需要在LoadBalancerClientFilter之前执行只修改lb://开头的服务调用URI保持原始请求的其他部分不变可以通过配置中心动态控制是否启用5. 进阶混合HTTPS/HTTP环境配置在某些复杂场景中可能需要网关同时支持对外HTTPS访问对内HTTP服务调用部分特殊服务需要HTTPS调用这种混合环境的配置示例如下server: port: 8443 ssl: enabled: true key-store: classpath:keystore.p12 key-store-password: changeit key-store-type: PKCS12 spring: cloud: gateway: routes: - id: http-service uri: lb:http://http-service predicates: - Path/http-api/** - id: https-service uri: lb:https://https-service predicates: - Path/https-api/** - id: default-route uri: lb:http://default-service predicates: - Path/**关键配置项server.ssl.enabled: 启用网关HTTPS按服务需求分别配置lb:http://和lb:https://默认路由建议使用HTTP除非明确知道服务支持HTTPS6. 排查技巧与常见误区即使按照上述方案配置仍可能遇到问题。以下是几个排查技巧检查清单确认网关确实接收的是HTTPS请求curl -vk https://gateway:8443/api/endpoint检查路由配置中的URI scheme# 错误示例 uri: lb://service-name # 正确示例 uri: lb:http://service-name查看网关日志中的实际转发URLlogging.level.org.springframework.cloud.gatewayDEBUG确认下游服务确实运行在HTTP端口常见误区认为网关配置HTTPS后所有通信都会自动加密实际上网关到服务的通信需要单独配置忽略服务发现场景下的scheme继承问题在Kubernetes环境中混淆Service类型ClusterIP vs NodePort忘记在测试环境关闭证书验证开发阶段可配置insecure-skip-verify7. 性能考量与最佳实践在实施HTTPS网关方案时还需考虑性能影响性能优化建议使用现代加密算法TLS 1.3server: ssl: protocol: TLSv1.3 ciphers: TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256启用OCSP Stapling减少证书验证开销考虑硬件SSL加速如AWS ALB监控网关的SSL握手指标# Netty原生指标 jcmd pid VM.native_memory | grep Ssl架构建议在网关层集中管理SSL/TLS终止内部服务间通信保持HTTP简化架构对特别敏感的服务可启用双向TLS认证定期轮换证书并自动化部署流程通过以上方案和最佳实践开发者可以彻底解决Spring Cloud Gateway中的NotSslRecordException问题同时构建出既安全又高性能的微服务通信架构。