WebClient自动添加请求头的位置排查及禁用方法咨询
问题:WebClient自动添加默认请求头的位置及禁用方法?
我发现WebClient会自动为请求添加默认头(比如HOST),想搞清楚这一行为的发生位置,同时需要禁用该功能。以下是复现问题的测试用例和依赖配置:
失败的测试用例
// 省略导入语句及pom基础配置 @Test void testIfNoHeadersAreAddedToRequestImplicitly() { Mono<Map<String, Object>> responseMono = WebClient.builder() .baseUrl("https://httpbin.org") .build() .get() .uri("/headers") .retrieve() .bodyToMono(new ParameterizedTypeReference<>() {}); StepVerifier.create(responseMono.map(m -> m.get("headers")).cast(Map.class)) .expectNextMatches(Map::isEmpty) .verifyComplete(); }
项目依赖配置
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.5</version> <relativePath/> <!-- 从仓库查找父依赖 --> </parent> <!-- ... --> <properties> <java.version>17</java.version> <spring-cloud.version>2022.0.4</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter</artifactId> </dependency> <!-- ↓ 包含WebFlux starter --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.wiremock</groupId> <artifactId>wiremock-standalone</artifactId> <version>3.5.1</version> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
我调试了DefaultWebClient.exchange()但没找到问题,后来在org.springframework.http.client.reactive.ReactorClientHttpConnector.connect(..)的回调里发现请求已经带有默认头,深入后发现这些头在Netty层面就已经存在了,而且WireMock里也能复现,排除了httpbin的问题。
分析与解决
1. 默认请求头的来源
- Netty底层自动填充:像
Host这类头是HTTP/1.1及以上协议强制要求的,Netty的HttpClient在构建DefaultHttpRequest时会自动从请求URI中提取Host信息填充,这部分逻辑在Netty的HttpClientCodec或HttpRequestEncoder相关代码里,属于底层协议实现的一部分,不是WebClient主动添加的。 - Spring生态的自动配置:如果依赖了Spring Cloud Gateway这类组件,其自动配置类(比如
GatewayAutoConfiguration)可能会通过WebClientCustomizer给WebClient添加默认头(比如User-Agent、X-Forwarded-*等);另外Spring WebFlux的默认配置也可能会添加一些通用头。
2. 禁用/修改默认头的方法
针对Netty自动添加的Host头
HTTP协议要求必须携带Host头,完全禁用不符合规范,但可以手动设置自定义Host来覆盖Netty的自动填充:
WebClient webClient = WebClient.builder() .baseUrl("https://httpbin.org") .defaultHeader(HttpHeaders.HOST, "your-custom-host") .build();
如果一定要绕过Netty的自动填充(不推荐),可以自定义Netty的HttpClient配置,通过ReactorClientHttpConnector传入自定义的HttpClient,并设置自定义的HttpHeadersFactory来控制头的生成。
清除WebClient的所有默认头
可以通过过滤器拦截请求,清除所有自动添加的头,只保留手动设置的部分:
WebClient webClient = WebClient.builder() .baseUrl("https://httpbin.org") .filter((request, next) -> { ClientRequest filteredRequest = ClientRequest.from(request) .headers(headers -> headers.clear()) // 这里可以手动添加需要的头 .header(HttpHeaders.HOST, "your-custom-host") .build(); return next.exchange(filteredRequest); }) .build();
排查Spring自动配置的自定义器
检查项目中是否有WebClientCustomizer类型的Bean,这些Bean会自动修改WebClient的默认配置。可以通过@SpringBootApplication(exclude = {WebClientAutoConfiguration.class})排除WebClient的自动配置,完全自定义WebClient的构建逻辑。
3. 调试关键点
- 在
DefaultClientRequestBuilder.build()方法加断点,跟踪请求头的构建过程,看哪些代码在添加头。 - 跟踪Netty的
HttpClient.send()方法,查看HttpRequest对象的初始化流程,确认默认头的添加位置。
内容的提问来源于stack exchange,提问作者Powet
相关产品推荐
相关产品推荐

