Avoiding Connection Failures with Proper HttpClient Singleton Usage
It is all about getting connected!

Avoiding Connection Failures with Proper HttpClient Singleton Usage

In distributed applications—whether you’re building APIs, workers, or integrations—outbound HTTP calls are mission-critical. But many developers unknowingly misuse the HttpClient class in .NET, leading to no session matched, socket exhaustion, intermittent timeouts, or "connection closed" errors.

Here bellow you can see a sample scenario of such situation which was visible by monitoring the traffic between the client and server applications:

Article content
Traffic monitoring

Let’s fix that once and for ever!

💥 Common Mistake: Creating HttpClient per Request

// ⚠️ Not recommended
using var client = new HttpClient();
var response = await client.GetAsync("https://api.example.com/data");        

This approach causes:

  • Excessive socket allocation.
  • DNS resolution issues.
  • Poor performance under load.
  • System resource leaks due to TCP TIME_WAIT.

✅ Recommended: Singleton or HttpClientFactory

Option 1: Reuse a Static HttpClient

public static class HttpClientFactory
{
    public static readonly HttpClient Instance = new HttpClient();
}        

Option 2: Use IHttpClientFactory (ASP.NET Core)

services.AddHttpClient(); // Or use named/typed clients        

Using HttpClientFactory is preferred in modern .NET, especially for resiliency and diagnostics.

🧠 Best Practices When Using HttpClient

Here’s how to avoid common traps and tune your clients for production:

1. Use SocketsHttpHandler for Fine-Grained Control

var handler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(5),
    KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always,
    KeepAlivePingDelay = TimeSpan.FromSeconds(30),
    KeepAlivePingTimeout = TimeSpan.FromSeconds(15)
};

var client = new HttpClient(handler);        

This ensures:

  • Connections are reused efficiently.
  • Stale DNS/IPs are refreshed.
  • Idle connections aren’t dropped unexpectedly.

2. Avoid DNS Staleness

Use PooledConnectionLifetime to force DNS refresh periodically.

⚙️ Advanced: Disabling Connection Pooling or Keep-Alive

In rare but valid cases, you might want to disable connection reuse or keep-alive entirely:

❌ Disable Connection Pooling

var handler = new SocketsHttpHandler
{
    EnableMultipleHttp2Connections = true,
    MaxConnectionsPerServer = int.MaxValue,
    PooledConnectionLifetime = TimeSpan.Zero, // ← forces new connection
    PooledConnectionIdleTimeout = TimeSpan.Zero
};        

This forces a fresh TCP connection for every request, which is useful:

  • During load testing to simulate fresh clients.
  • When dealing with badly behaving intermediaries.
  • In short-lived CLI tools or cold-start scenarios.

❌ Disable Keep-Alive

var request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com");
request.Headers.ConnectionClose = true;        

Or via handler:

var handler = new SocketsHttpHandler
{
    KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Never
};        

Disabling keep-alive can be appropriate when:

  • Firewalls/load balancers force-close idle connections.
  • You don't need persistent sessions.
  • You're troubleshooting connection reuse issues.

🔒 Warning: Disabling pooling or keep-alive should be done carefully—it increases overhead and can degrade performance significantly in high-volume environments.

🔍 Real-World Impact

After adopting these best practices, it is visible that:

  • Teams saw much fewer transient HTTP failures.
  • Socket exhaustion problems disappeared entirely or for a big part.
  • Systems scaled more reliably under bursty traffic.

📌 Summary

Here you can find a summary of the best practices that can be followed to approach every concern we talked about here in this article:

  • Socket exhaustion : Use singleton or HttpClientFactory
  • DNS staleness : Set PooledConnectionLifetime
  • Idle connection drops : Use KeepAlivePingPolicy and timeouts
  • Debugging reuse issues : Temporarily disable connection pooling
  • Short-lived tools/apps : Consider disabling keep-alive

Sample of HttpClientFactory implementation:

// DISCLAIMER: The sample code and configurations presented in this article are provided as-is, without any warranties or guarantees. They are intended for educational and illustrative purposes only. Before applying any of these examples in a production environment, you must evaluate and validate them in the context of your specific use case, architecture, and operational requirements.

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;

public static class HttpClientFactory
{
    public static HttpClient CreateClient()
    {
        // Create a new handler for each client to avoid
        // pooled connections
        var handler = new SocketsHttpHandler
        {
            AutomaticDecompression = DecompressionMethods.GZip |     
                                     DecompressionMethods.Deflate,

            // Disable connection pooling to prevent reuse of sockets
            EnableMultipleHttp2Connections = false,
            // no limit, since each request will use new handler
            MaxConnectionsPerServer = int.MaxValue, 
            PooledConnectionLifetime = TimeSpan.Zero,
            PooledConnectionIdleTimeout = TimeSpan.Zero,
            KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Never
        };

        var client = new HttpClient(handler, disposeHandler: true)
        {
            Timeout = TimeSpan.FromMinutes(2)
        };

        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
          new MediaTypeWithQualityHeaderValue("application/json"));

        // Instruct server to close the connection after the request
        client.DefaultRequestHeaders.ConnectionClose = true;

        return client;
    }
}        
// ✅ Shared HttpClient
private static readonly HttpClient _httpClient = HttpClientFactory.CreateClient();         

✨ Final take away

The HttpClient isn’t just a utility—it’s a critical component that, if misused, can undermine your app’s stability. Build good logging and traceability's to be able to pin point connection failures in a proper way.


Disclaimer

The sample code and configurations presented in this article are provided as-is, without any warranties or guarantees. They are intended for educational and illustrative purposes only. Before applying any of these examples in a production environment, you must evaluate and validate them in the context of your specific use case, architecture, and operational requirements.

References

Have you faced the real problem with it? Because, as I understand, HttpClient is designed to be disposed and created per session, not doing this may lead to some other problems, e.g. https://byterot.blogspot.com/2016/07/singleton-httpclient-dns.html . It will be interesting to know, on what scale it may stop working

To view or add a comment, sign in

More articles by Mehrdad Ghazvinizadeh

Others also viewed

Explore content categories