You migrate to Spring Boot 3.2, maybe add spring-boot-starter-webflux along the way, and at runtime you get this:
java.lang.NoClassDefFoundError: org/apache/http/conn/ssl/SSLConnectionSocketFactory
It’s easy to blame the new starter, but WebFlux is almost never the cause. Here’s what the error actually means and how to fix it.
What the error means
The package name tells you everything:
org.apache.http...is Apache HttpClient 4.x.org.apache.hc...is Apache HttpClient 5.x.
Spring Boot 3 and Spring Framework 6 are built around HttpClient 5. If your code (or a library you use) still calls the 4.x classes, and nothing in your dependency tree provides them anymore, the JVM can’t find them and throws NoClassDefFoundError.
Is spring-boot-starter-webflux the culprit?
No. The WebFlux starter brings Spring WebFlux, JSON support and Reactor Netty. It has never included Apache HttpClient, 4.x or 5.x, and it doesn’t exclude it either.
What usually happens is this: during the Boot 2 → 3 migration, a dependency that used to bring HttpClient 4.x in transitively changed or went away. WebFlux just happened to be added in the same change.
One more note: if your project has both spring-boot-starter-web and spring-boot-starter-webflux, Spring Boot runs the servlet stack (Tomcat) by default. Netty only becomes the server when WebFlux is the only web starter.
Step 1: find who needs the class
Start with the stack trace. The first frame outside the JDK usually points at the library or class calling HttpClient 4.x. Then check what’s on the classpath:
./mvnw dependency:tree -Dincludes=org.apache.httpcomponents
For Gradle:
./gradlew dependencyInsight --dependency httpclient --configuration runtimeClasspath
If nothing from org.apache.httpcomponents:httpclient shows up, that’s your answer: 4.x is missing.
Fix 1: add HttpClient 4.x explicitly
If a third-party library needs it (or you can’t change the code yet), declare it yourself:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
implementation("org.apache.httpcomponents:httpclient:4.5.14")
This is the fastest fix and it’s safe to run 4.x and 5.x side by side, since they use different packages. Keep in mind that 4.5.x only gets maintenance updates, so scan it for vulnerabilities (for example with org.owasp:dependency-check-maven).
Fix 2: migrate your code to HttpClient 5
If the code is yours, moving to 5.x is the cleanest option. The SSL setup changed: you configure TLS on the connection manager, not on the client builder.
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.ssl.SSLContexts;
var tlsStrategy = new DefaultClientTlsStrategy(SSLContexts.createSystemDefault());
PoolingHttpClientConnectionManager connectionManager =
PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(tlsStrategy) // 5.2+
.build();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
Spring Boot manages the org.apache.httpcomponents.client5:httpclient5 version for you, so you can usually omit it in your pom.xml.
Fix 3: use WebClient
If you added WebFlux anyway, consider replacing the outbound calls with WebClient. It’s non-blocking and doesn’t need Apache HttpClient at all:
WebClient client = WebClient.create("https://api.example.com");
Mono<String> body = client.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class);
If you have to keep a blocking client inside a reactive pipeline, don’t run it on the event loop. Move it to a separate scheduler:
Mono.fromCallable(() -> blockingCall())
.subscribeOn(Schedulers.boundedElastic());
Check that the fix worked
A smoke test that just creates the client is enough to catch the missing class, with no network involved:
@Test
void httpClientClassesAreOnTheClasspath() {
assertDoesNotThrow(() -> HttpClients.createDefault().close());
}
Then start the app (./mvnw spring-boot:run) and hit an endpoint that makes an outbound call.
FAQ
Why does the error mention SSL if I only call plain HTTP?
HttpClient 4.x’s builder sets up an SSL socket factory by default, even for http:// URLs, so the class has to be on the classpath either way.
Can I run HttpClient 4.x and 5.x together?
Yes. They use different packages (org.apache.http vs org.apache.hc), so they don’t conflict.
Is HttpClient 4.x safe to keep using?
It only receives maintenance updates. It’s fine as a temporary fix, but plan to move to 5.x or WebClient, and scan your dependencies regularly.
Wrapping up
When a NoClassDefFoundError shows up after a Spring Boot upgrade, look at the package name before blaming the last starter you added. org.apache.http points to HttpClient 4.x, which Boot 3 no longer relies on. Add it back as a stopgap, or migrate to 5.x or WebClient.
More Spring Boot migration fixes coming soon on SpiritCode.

