Configuration
About
WebClient is Spring's non-blocking, reactive HTTP client designed for both synchronous and asynchronous communication. While using WebClient out of the box is sufficient for many cases, real-world applications often require fine-tuned configuration — including timeouts, connection pools, SSL settings, base URLs, and default headers.
Proper configuration ensures consistent behavior across all requests, improves performance, and avoids repetition of setup logic.
Configuration Aspects
Base URL
Define a root URL so we don’t repeat it on every request
Default Headers / Cookies
Apply common headers or cookies globally (like Auth headers)
Timeouts
Prevent hanging requests due to slow downstream services
Connection Pooling
Efficient reuse of TCP connections
Custom Codecs
Customize serialization/deserialization behavior
Proxy Settings
Enable routing through a proxy (e.g., for logging or security)
SSL Configuration
Trust specific certificates or apply secure connection settings
ExchangeFilterFunction
Add interceptors for logging, metrics, tracing, authentication etc.
Reuse and Centralization
In enterprise-grade applications, it’s important to avoid repeated and inconsistent configuration of WebClient across different services and modules. This is where the principle of centralized configuration and client reuse becomes critical.
Rather than instantiating WebClient ad hoc using WebClient.create() or re-writing builder logic in every service class, it’s considered best practice to define named, reusable WebClient beans in a dedicated configuration class. This promotes consistency, eases maintenance, and enables standardized behavior across services.
Typical Centralized Structure
We define a Spring @Configuration class where multiple specialized clients are exposed:
Now, we can inject them cleanly:
Central Registry or Factory
In some projects, a dynamic client registry or factory might be used if endpoints are determined at runtime, or for multitenant use:
This approach is useful when our application communicates with different third-party systems whose properties are stored in a DB or config server.
Base URL and Default Headers
We can configure a WebClient bean with a base URL and default headers that apply to all requests.
This reduces boilerplate and ensures consistent header usage across requests.
Timeout Settings
By default, WebClient uses Reactor Netty under the hood, and it does not apply any connection, read, or write timeout unless explicitly configured. In real-world systems especially those integrated with external services timeouts are critical for preventing resource exhaustion, request pile-up, and degraded performance.
We can configure timeouts using the underlying HttpClient (from Reactor Netty) and wire that into our WebClient.
Timeout Types
Connection Timeout
Maximum time to establish a TCP connection to the server.
Read Timeout
Maximum time to wait for data to be read from the server.
Write Timeout
Maximum time to wait while sending data to the server.
Response Timeout
Maximum time to wait for a complete response from the server.
Explanation
CONNECT_TIMEOUT_MILLIS– How long to wait to establish the TCP connection.responseTimeout(Duration)– How long to wait for the entire response (headers + body).ReadTimeoutHandler/WriteTimeoutHandler– Netty-level handlers that apply read/write I/O timeouts after connection is established.
Connection Pooling
Connection pooling allows the reuse of existing TCP connections rather than opening a new connection for every request. This significantly improves performance and resource utilization, especially in high-throughput or service-to-service communication scenarios.
By default, WebClient uses Reactor Netty, which provides a non-blocking connection pool. However, pooling is not enabled unless explicitly configured.
Why Connection Pooling Matters
Reduces latency
Avoids TCP and TLS handshake overhead for every request.
Improves throughput
Multiple requests can be processed efficiently using persistent connections.
Conserves system resources
Limits number of open sockets and threads required.
Aligns with modern microservices
Essential for high-performance internal service calls.
Enabling Connection Pooling with WebClient (Reactor Netty)
Connection pooling is done via the ConnectionProvider API in Reactor Netty. Here's a standard setup:
Key Configuration Options
maxConnections
Maximum active connections in the pool
pendingAcquireMaxCount
How many queued requests can wait for a connection
pendingAcquireTimeout
Maximum time to wait for an available connection before failing
maxIdleTime
Closes idle connections after a certain duration
maxLifeTime
Closes connections after a set lifetime (to avoid stale TCP connections)
How It Works Internally ?
Connections are created as needed (up to the max).
Idle connections are reused for new requests.
If no connection is available and the queue is full, requests fail immediately.
Debugging and Monitoring
Enable the following Reactor Netty logs to understand connection usage:
We can also observe connection reuse patterns via APM tools or custom metrics.
Adding Logging, Tracing, or Retry Using Filters
Spring WebClient supports a powerful feature called filters, which are analogous to servlet filters or interceptors. These allow us to intercept and modify requests and responses, making them ideal for cross-cutting concerns such as:
Request and response logging
Distributed tracing propagation (e.g., Sleuth, OpenTelemetry)
Retry mechanisms
Metrics collection
Error handling
What Is a WebClient Filter ?
A filter is a function applied to every WebClient call that allows us to inspect, log, modify, or retry the request/response pipeline.
Signature:
We typically add filters at the WebClient builder level.
1. Logging Requests and Responses
Register the filters
2. Distributed Tracing (Spring Cloud Sleuth / OpenTelemetry)
WebClient integrates with Spring Cloud Sleuth or OpenTelemetry automatically, provided the tracing context is active.
For manual tracing propagation (custom headers):
In enterprise setups, Sleuth auto-injects trace IDs via TraceExchangeFilterFunction, so we often don’t need to customize this.
3. Retry Logic via Resilience4j or Reactor Retry
a. Basic Retry Example with retryWhen
retryWhenb. Retry with Resilience4j Filter
This allows retries with circuit-breaking, fallback logic, and custom backoff.
4. Combine Filters in Centralized Builder
This setup ensures all requests passing through this WebClient instance include standardized logging, tracing headers, and can be wrapped in retry/circuit-breaker logic.
Proxy Configuration
In enterprise environments, it's common to operate behind an HTTP proxy for security, compliance, or traffic routing purposes. WebClient supports proxy configuration through its underlying HttpClient (from Reactor Netty).
Configuring a proxy allows our WebClient-based applications to:
Route outbound HTTP calls through a proxy server
Enforce outbound firewall rules
Perform internal service routing in secured networks
Support testing in proxy environments (e.g., Charles Proxy, Fiddler)
Example
This configures WebClient to route all requests through the specified HTTP proxy.
Proxy with Authentication
If the proxy requires basic authentication:
Note: Avoid hardcoding credentials; use secure storage like environment variables, Spring Vault, or secret managers.
Proxy Types Supported
HTTP
Standard HTTP proxy
SOCKS4 / SOCKS5
SOCKS proxy for TCP-based protocols
DIRECT
No proxy (default if not configured)
We can choose the type using:
Use Spring profiles to isolate proxy logic:
Then conditionally enable the proxy WebClient for production profile only.
Debugging Proxy Issues
Use Wireshark or tcpdump to verify traffic redirection.
Use WebClient logging or custom filters to log connection metadata.
Test using curl or Postman with proxy settings for comparison.
Custom Message Converters
By default, WebClient uses built-in message readers and writers to handle data formats like JSON or XML. These are based on HTTP message converters, most commonly using Jackson for JSON. However, in some enterprise scenarios, we may need to register or override default converters, such as:
Custom serialization/deserialization logic
Supporting non-standard or proprietary media types
Modifying behavior for specific content types (e.g., parsing HAL, CSV, or binary formats)
How Message Conversion Works in WebClient ?
When a WebClient makes a request or receives a response:
Writers are used to encode Java objects into the request body
Readers are used to decode response bodies into Java objects
Spring uses ReactiveHttpMessageReader and ReactiveHttpMessageWriter interfaces for this under the hood.
Use Cases for Custom Converters
Custom JSON field naming or formatting
When default Jackson rules don’t suffice
CSV or custom-text format
For legacy APIs or non-JSON-based protocols
Encrypted payloads
To decrypt/encrypt request/response body
Enriching or modifying object before deserialization
To transform inputs to domain-specific classes
Registering Custom Converters
WebClient allows us to configure custom message converters through the underlying ExchangeStrategies.
Example: Custom Jackson Module
We can register additional modules like JavaTimeModule, JodaModule, or our own.
Example: Add Support for Custom Media Type (e.g., CSV)
Then:
Override vs Extend Default Behavior
Tweak Jackson’s behavior
Register a custom ObjectMapper
Replace JSON handling entirely
Remove default codecs and register our own
Add support for new format (e.g., Avro)
Add custom reader/writer to customCodecs()
Intercept encoding/decoding
Create decorator around existing Jackson encoder/decoder
Custom ExchangeStrategies
This gives us complete control over:
Logging
Media type resolution
Reactive flow customization
Logging
1. Enable Basic WebClient Logging
Logs request method, URI, status code, and error signals.
Example:
2. Enable Reactor Netty HTTP Logging
a) Request & Response details
Logs connection events, request sent, response received.
Example:
b) Full wire-level logging (headers + body)
Shows raw bytes over TCP (like
org.apache.http.wirein HttpClient).Example:
3. Enable Codec & JSON Logging
To see what’s happening inside message encoders/decoders:
Logs when Spring encodes request/response bodies.
Shows serialization details if using Jackson.
Last updated