← Back to Articles         
Problems with Direct HttpClient Usage

Problems with Direct HttpClient Usage

Published on 28 Sep 2026     6 min read Microservices
Microservice Design

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 HttpClient for every HTTP request.
  • HttpClient uses 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.
  • IHttpClientFactory manages 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, IHttpClientFactory is 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 HttpClient itself; repeatedly creating and disposing it can cause connection/port exhaustion, while poorly configured long-lived clients can have DNS issues. IHttpClientFactory provides managed handler lifetimes, connection reuse, centralized configuration, and resilience integration.

← Back to Articles