What Are the Problems with Directly Using HttpClient?
In ASP.NET Core, HttpClient itself is not bad. The main problem is incorrectly creating and managing HttpClient instances, especially creating a new one for every request.
For microservice-to-microservice communication, IHttpClientFactory is usually a better approach.
1. What Does “Directly Using HttpClient” Mean?
A common implementation is:
public async Task<string> GetProductAsync()
{
using var client = new HttpClient();
return await client.GetStringAsync(
"https://product-service/api/products/10");
}
Every time the method executes:
Request 1 → new HttpClient()
Request 2 → new HttpClient()
Request 3 → new HttpClient()
Request 4 → new HttpClient()
This repeated creation/disposal pattern can cause problems in high-traffic applications.
2. Why Is This a Problem?
HttpClient uses an underlying HTTP handler, such as SocketsHttpHandler, which manages a connection pool.
Conceptually:
HttpClient
↓
HttpMessageHandler
↓
Connection Pool
↓
TCP Connections
↓
Product Service
Disposing HttpClient does not necessarily mean all underlying TCP resources immediately disappear.
TCP connections can remain in states such as TIME_WAIT for some time.
With many requests:
Create Client
↓
Open Connections
↓
Dispose Client
↓
Repeat thousands of times
This can put pressure on available ephemeral ports and lead to socket/port exhaustion.
3. Problem 1 — Socket/Port Exhaustion
Consider:
using var client = new HttpClient();
await client.GetAsync(url);
If this happens thousands of times under load, the application can create excessive connection churn.
Eventually, you may encounter connection failures because available networking resources are exhausted.
Better
Reuse connections through a properly managed HttpClient strategy, such as:
builder.Services.AddHttpClient<ProductApiClient>();
IHttpClientFactory manages and pools underlying handlers.
4. Problem 2 — DNS Changes
There is another side to the problem.
Suppose you solve the first issue by creating one HttpClient and keeping it forever:
private static readonly HttpClient _client =
new HttpClient();
This avoids repeatedly creating clients, but long-lived connection pools need to account for DNS changes.
For example:
product-service
↓
10.10.1.10
After deployment or infrastructure changes:
product-service
↓
10.10.1.25
If existing connections remain alive for too long, the application may continue communicating through old connections rather than promptly using the newly resolved endpoint.
IHttpClientFactory helps by managing handler lifetimes.
5. Problem 3 — Configuration Gets Scattered
Without centralized configuration, developers may write:
var client = new HttpClient();
client.BaseAddress =
new Uri("https://product-service/");
client.Timeout =
TimeSpan.FromSeconds(10);
client.DefaultRequestHeaders.Add(
"X-Api-Key", "...");
Then similar code appears elsewhere:
OrdersController
ProductsService
PaymentService
InventoryService
...
This creates duplicated configuration.
With IHttpClientFactory:
builder.Services.AddHttpClient<ProductApiClient>(client =>
{
client.BaseAddress =
new Uri("https://product-service/");
client.Timeout =
TimeSpan.FromSeconds(10);
});
Configuration is centralized.
6. Problem 4 — Resilience Becomes Harder to Standardize
Network calls can fail:
Order Service
│
X
Product Service
Possible failures include:
- Timeout
- Temporary network error
- Service unavailable
- Connection failure
- Server overload
You may need:
Timeout
Retry
Circuit Breaker
If every developer creates HttpClient manually, these policies can become inconsistent.
With factory-configured clients, resilience policies can be configured centrally.
7. Problem 5 — Harder Dependency Injection
Suppose your service creates the dependency itself:
public class ProductService
{
public async Task GetProduct()
{
var client = new HttpClient();
// ...
}
}
ProductService controls the creation of its dependency.
That makes configuration and testing less clean.
With DI:
public class ProductApiClient
{
private readonly HttpClient _client;
public ProductApiClient(HttpClient client)
{
_client = client;
}
}
ASP.NET Core supplies the configured HttpClient.
8. Problem 6 — Harder Testing
Direct construction:
var client = new HttpClient();
couples your code to a concrete client created inside the method.
It becomes harder to substitute the HTTP behavior during unit testing.
A typed client or an abstraction such as:
public interface IProductApiClient
{
Task<ProductDto?> GetProductAsync(int id);
}
makes application code easier to isolate and test.
9. Problem 7 — Logging and Observability
In microservices, understanding outbound calls is important:
Order Service
│
│ HTTP
▼
Product Service
You may need information about:
- Request duration
- Response status
- Failures
- Retries
- Distributed traces
- Correlation information
Factory-created clients integrate naturally with ASP.NET Core's logging and HTTP pipeline.
10. Is new HttpClient() Always Wrong?
No.
This is an important interview point.
The issue isn't simply:
"
new HttpClient()is always wrong."
A properly configured long-lived HttpClient can be valid.
The problematic pattern is primarily:
using var client = new HttpClient();
for every request/operation.
For typical ASP.NET Core microservices, IHttpClientFactory provides a convenient framework-managed solution.
11. Direct HttpClient vs IHttpClientFactory
| Direct/poorly managed approach | IHttpClientFactory |
|---|---|
| Easy to create clients repeatedly | Managed client creation |
| Can cause connection churn | Handler pooling |
| Risk of port exhaustion | Better connection management |
| Long-lived pools require DNS consideration | Handler lifetime management |
| Configuration can be duplicated | Centralized configuration |
| Resilience may be scattered | Central resilience configuration |
| Manual organization | Named/typed clients |
| Less framework integration | DI and logging integration |
12. Advantages of Direct HttpClient
Direct HttpClient does have some advantages:
- Very simple for small applications
- Minimal configuration
- Easy for quick prototypes
- A correctly configured long-lived instance can work efficiently
So HttpClient itself is not the problem; its lifetime and configuration must be managed correctly.
13. Disadvantages
If used incorrectly:
- Socket/port exhaustion
- Excessive connection creation
- DNS/network endpoint issues with poorly managed long-lived pools
- Duplicated configuration
- Harder resilience management
- Harder dependency management
- Harder testing
- Less centralized logging/observability
14. Key Points
- Don't create and dispose
HttpClientfor every HTTP request. HttpClientuses underlying handlers and connection pools.- Frequent handler creation can cause connection churn and port exhaustion.
- A single long-lived client avoids connection churn but needs appropriate connection/DNS lifetime configuration.
IHttpClientFactorymanages handler lifetime and pooling.- It supports named and typed clients.
- It integrates with DI and logging.
- It provides a clean location for timeouts, retries and circuit breakers.
- In ASP.NET Core microservices,
IHttpClientFactoryis commonly the preferred approach.
15. Interview Questions & Answers
Q1. What is the main problem with directly using HttpClient?
Answer:
The main problem is incorrect lifetime management. Creating and disposing a new HttpClient for every operation can create excessive connection churn and eventually cause socket/port exhaustion.
Q2. Why shouldn't we create new HttpClient() for every request?
Answer:
Because each client's underlying handler manages network connections. Repeated creation and disposal can prevent efficient connection reuse and put pressure on available network ports.
Q3. Is HttpClient itself bad?
Answer:
No. HttpClient is designed to reuse connections. The problem is usually how its lifetime and underlying handlers are managed.
Q4. Can we use one static HttpClient?
Answer:
Yes, a long-lived HttpClient can be valid. However, its connection lifetime should be configured appropriately so that DNS changes can be respected.
Q5. How does IHttpClientFactory solve these problems?
Answer:
It manages and pools underlying HTTP handlers, centralizes client configuration, integrates with DI and logging, and provides a convenient foundation for resilience strategies.
Interview One-Line Answer
The problem is not
HttpClientitself; repeatedly creating and disposing it can cause connection/port exhaustion, while poorly configured long-lived clients can have DNS issues.IHttpClientFactoryprovides managed handler lifetimes, connection reuse, centralized configuration, and resilience integration.