Blog

Fixing HttpClient Memory Leaks: When IDisposable Isn’t Enough

If you've been instantiating HttpClient directly in your .NET applications, you might be sitting on a time bomb. Even with proper using statements, creating multiple HttpClient instances can drain your server's available sockets and cause connectivity issues in production. The solution comes in the form of HttpClientFactory, a component that manages the lifecycle of HTTP message handlers so you don't have to.

This isn't just a theoretical problem. Developers building anything from background sync services to API clients encounter socket exhaustion without realizing what's happening until production breaks at the worst moment.

The Problem: Why Direct Instantiation Wreaks Havoc

Socket exhaustion happens because operating systems treat network sockets as finite resources. When your code disposes an HttpClient, the underlying socket doesn't return to the pool immediately—it lingers in a TIME_WAIT state for several minutes. In high-throughput scenarios, this adds up fast.

Here's the trap most developers fall into:

Each invocation leaves behind a handler that's waiting to time out, and the sockets attached to those handlers become unavailable until the operating system forcibly reclaims them. Even wrapping the client in a using statement doesn't help because the handler, not the client, holds the socket hostage.

The real culprit isn't the HttpClient itself. It's the HttpMessageHandler attached to each instance. Handlers hold onto sockets, and you have zero control over their cleanup when using HttpClient directly.

HttpClientFactory: The Built-in Solution

Microsoft introduced IHttpClientFactory specifically to address this. Instead of creating HttpClient instances yourself, you inject an IHttpClientFactory into your classes and call CreateClient() whenever you need one. The factory manages handler pooling behind the scenes, reusing instances and disposing them on a proper schedule.

Setting it up takes one line in your Startup or Program class:

The extension method lives in Microsoft.Extensions.DependencyInjection, so if you're working with ASP.NET Core or any modern .NET project, it's already available. You can also add it to worker services, console applications, and just about any .NET project type that uses dependency injection.

Beyond fixing socket exhaustion, HttpClientFactory offers a centralized place to configure defaults. You can set base URLs, common headers, timeout values, and authentication once—then every client your factory produces inherits those settings automatically.

Why This Matters for Your Projects

Socket exhaustion manifests differently depending on your app type. In web APIs, you might see intermittent 500 errors or complete crashes under load. Background services often just stop processing silently. Desktop apps can freeze or throw obscure network exceptions.

For Android developers building apps with .NET MAUI or Xamarin, the picture gets more interesting. Mobile apps frequently make multiple concurrent API calls. Users might have several apps hitting different endpoints simultaneously. And Android devices have constrained resources compared to servers.

Say you're building a weather app that fetches forecasts every 15 minutes using a background service. If you create a fresh HttpClient each time, you'll exhaust available sockets within a few hours of normal use. Your app starts failing to refresh, users blame your API, and you're left debugging a symptom instead of the root cause.

Using IHttpClientFactory in that same scenario means the factory maintains a small pool of handlers, reusing them across all your network calls and cleaning them up properly. Your app stays responsive, your battery lasts longer, and you never worry about socket limits.

What to Watch Next

Microsoft has been steadily improving HttpClientFactory. Recent versions include better logging, support for typed clients, and tighter integration with Polly for retry policies. If you're still creating HttpClient instances manually, now's the time to migrate.

Key takeaways

  • Never create HttpClient instances directly in methods that get called frequently
  • Inject IHttpClientFactory instead and call CreateClient() for each request
  • Configure centralized defaults through the factory rather than per-instance
  • The handler lifecycle is what actually matters—not the HttpClient disposal
  • Named clients let you define different configurations for different endpoints

Frequently asked questions

Can I still use "using" statements with HttpClientFactory-created clients?

You technically can, but it's unnecessary. The factory handles handler disposal, so you're only cleaning up the HttpClient wrapper, which is largely symbolic. Let the factory manage the lifecycle.

Does HttpClientFactory work with Blazor, Xamarin, or MAUI?

Yes, as long as you're using .NET Core 3.0 or later and have the Microsoft.Extensions.Http package installed. It works across mobile, web, and desktop scenarios.

What's the performance impact of switching to HttpClientFactory?

You should see improved performance in any scenario where you're making frequent HTTP calls. Socket reuse eliminates the connection overhead that comes with fresh instances.

Can I configure retry policies with HttpClientFactory?

Yes. HttpClientFactory integrates with Polly, Microsoft's resilience library. You can define retry logic, circuit breakers, and timeouts that apply to all clients created through the factory.

Are there any downsides to using HttpClientFactory?

The main trade-off is added complexity. For very simple scripts or one-off requests, creating HttpClient directly is fine. The factory shines in applications that make repeated network calls.

Related