Back to Blog
spring-bootresttemplatewebclientjavaperformancehttp-client

RestTemplate Without Timeouts in Spring Boot: The Hidden Outage Risk (2026)

RestTemplate with no timeout configuration will hang forever when a downstream service is slow. Learn how to set correct timeouts and migrate to WebClient or RestClient.

J

JOptimize Team

May 21, 2026· 7 min read

A RestTemplate with no timeout configuration will wait forever for a slow downstream service. One hung HTTP call holds a thread. Under load, every thread holds. Your thread pool exhausts. Your entire application stops responding - not because your code is broken, but because a dependency is slow.


The Default Behavior: No Timeout

// This will hang indefinitely if the payment service is slow @Service public class PaymentService { private final RestTemplate restTemplate = new RestTemplate(); // No timeout! public PaymentResponse charge(PaymentRequest request) { return restTemplate.postForObject( "https://payment-api.example.com/charge", request, PaymentResponse.class ); // Thread blocked here forever if service hangs } }

The default RestTemplate uses SimpleClientHttpRequestFactory which has:

  • connectTimeout = -1 (infinite)
  • readTimeout = -1 (infinite)

In production, "infinite" means "until the OS TCP keepalive kicks in" - which can be hours.


The Cascading Failure Pattern

1. Payment service starts responding slowly (1 request takes 30s)
2. Your app has 50 threads in the Tomcat pool
3. 50 concurrent users hit the payment endpoint
4. All 50 threads are blocked waiting for payment response
5. Thread 51 arrives - queued
6. Thread pool queue fills up
7. New requests rejected with 503
8. Your entire app is down - not just the payment feature

This is why timeout configuration is not optional in production.


Fix 1: Add Timeouts to RestTemplate

@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(3000); // 3s to establish connection factory.setReadTimeout(10000); // 10s to read response factory.setConnectionRequestTimeout(2000); // 2s to get from pool return new RestTemplate(factory); } }

For per-client configuration (different timeouts per downstream service):

@Configuration public class HttpClientConfig { @Bean("paymentRestTemplate") public RestTemplate paymentRestTemplate() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(2000); factory.setReadTimeout(5000); // Payment must respond in 5s return new RestTemplate(factory); } @Bean("reportingRestTemplate") public RestTemplate reportingRestTemplate() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(2000); factory.setReadTimeout(60000); // Reports can take up to 60s return new RestTemplate(factory); } }

Fix 2: Migrate to RestClient (Spring Boot 3.2+)

RestTemplate is in maintenance mode. Spring Boot 3.2 introduced RestClient as its synchronous replacement, with a cleaner API and built-in timeout support:

@Configuration public class RestClientConfig { @Bean public RestClient paymentClient() { return RestClient.builder() .baseUrl("https://payment-api.example.com") .requestFactory(clientHttpRequestFactory()) .defaultHeader("Content-Type", "application/json") .build(); } private ClientHttpRequestFactory clientHttpRequestFactory() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(3000); factory.setReadTimeout(10000); return factory; } } @Service @RequiredArgsConstructor public class PaymentService { private final RestClient paymentClient; public PaymentResponse charge(PaymentRequest request) { return paymentClient.post() .uri("/charge") .body(request) .retrieve() .onStatus(HttpStatusCode::is4xxClientError, (req, res) -> { throw new PaymentClientException("Bad request: " + res.getStatusCode()); }) .onStatus(HttpStatusCode::is5xxServerError, (req, res) -> { throw new PaymentServerException("Payment service error"); }) .body(PaymentResponse.class); } }

Fix 3: Add a Circuit Breaker with Resilience4j

Timeouts stop threads from blocking forever. Circuit breakers stop your app from even trying when a service is down:

<dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot3</artifactId> </dependency>
@Service public class PaymentService { @CircuitBreaker(name = "payment", fallbackMethod = "paymentFallback") @TimeLimiter(name = "payment") public CompletableFuture<PaymentResponse> charge(PaymentRequest request) { return CompletableFuture.supplyAsync(() -> restClient.post().uri("/charge").body(request) .retrieve().body(PaymentResponse.class) ); } public CompletableFuture<PaymentResponse> paymentFallback( PaymentRequest request, Exception ex) { log.error("Payment service unavailable: {}", ex.getMessage()); return CompletableFuture.completedFuture( PaymentResponse.queued(request.getId()) ); } }
# application.yml resilience4j: circuitbreaker: instances: payment: failure-rate-threshold: 50 wait-duration-in-open-state: 30s sliding-window-size: 10 timelimiter: instances: payment: timeout-duration: 5s

Common Mistakes to Avoid

  • Using the default RestTemplate bean in production - Spring Boot auto-configures a RestTemplate via RestTemplateBuilder, but it has no timeouts unless you configure them
  • Setting only readTimeout - connectTimeout also matters; a SYN_SENT socket can hang for minutes without it
  • Same timeout for all services - a report generation endpoint needs a 60s timeout; a health check needs 1s; configure per-service
  • Not handling ResourceAccessException - this is what Spring throws on timeout; if you don't catch it, it surfaces as a 500 error with no useful message to the caller

Summary

RestTemplate without timeouts is a production outage waiting to happen. Add connectTimeout and readTimeout via HttpComponentsClientHttpRequestFactory, or migrate to RestClient (Spring Boot 3.2+) with explicit factory configuration. For critical dependencies, add Resilience4j circuit breakers to fail fast and recover gracefully.


Detect Missing Timeouts in Your Codebase

JOptimize scans your Spring Boot project for RestTemplate beans with no timeout configuration and RestClient builders with no request factory - and flags them with suggested fixes.

Catch missing timeouts before they cause a cascading outage - free scan, no configuration required.

Want to go deeper?

Master Spring Boot, security, and Java performance with hands-on courses.

Detect issues in your project

JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.