Using HttpClient in Azure Functions

Published by Nicolas POPEK
Category : .Net / Azure Functions
18/06/2025

Aaah, HttpClients. There are plenty of reasons to use them—regardless of the language.
All it takes is the need to call a resource on the internet—be it a web page or an API—and boom! You’re going through an HttpClient.

And yet, while it’s a core component of the .NET library, it’s also a topic full of gotchas, and it’s very easy to use it incorrectly. Especially in Azure Functions.

But don’t panic—MiddleWay to the rescue!
Let me walk you through how to use them properly.
But first… a little explanation as to why it’s so easy to get it wrong.

 

The Big Gotcha Around HttpClient

 

HttpClient is a wrapper around a message handler.
That infamous… HttpMessageHandler. You can actually see it in the overloads of the HttpClient constructor.

 

HttpClient Constructor

 

And here lies the trap. When you use the default constructor, well, HttpClient creates a new handler. Every. Single. Time. Yep—every time.
And when the instance is destroyed, the handler is destroyed… but not entirely. In reality, the handler keeps its TCP ports open, just in case it still needs to receive some HTTP packets!

It keeps those ports open for a configurable duration, which by default is… infinite. Yes, infinite!
The ports will stay open until the application is restarted. It’s even documented—look it up!

 

HttpClient Documentation

 

In a one-shot application that only creates one client, that’s okay, because the risk is low. When the app restarts, ports get released.

But what about a project that makes a lot of requests? Or that needs to run 24/7?

You guessed it. You’ll start opening ports, keep them open… and eventually you’ll run out of available ports. You won’t be able to create new HttpClient instances.
That’s what we call Port Exhaustion.

Your flow will crash because no port is left.
And in Azure? Even worse. The ports are not scoped to your Function App—they’re scoped to your App Service Plan.
Port Exhaustion on an App Service Plan? That means all apps on that plan that use ports will go down. A situation we all want to avoid.

There’s plenty of documentation out there, and Microsoft has even published a guide on the topic.

But I’m going to break it down for you here.

 

Solving the Problem

 

You might be thinking:
“Well okay then, I’ll just create a single client and reuse it over and over.”
And… yes! That’s actually one way to fix the issue.
With only one handler, there’s no risk of exhausting the ports.

However, the handler depends on resources that eventually expire. For instance: DNS leases!
A well-known issue is getting an error like “Host not found” even though the host is perfectly reachable. That’s because the handler has an expired DNS lease and needs to be recycled.

That’s why you can pass a custom handler to your client—especially one where you set the PooledConnectionLifetime property.
Setting it to, say, 15 minutes solves the issue:
The ports will only stay open for a maximum of 15 minutes, and after that, the next HTTP call will recycle the handler, making it functional again.

 

internal static readonly HttpClient HTTP_CLIENT = new HttpClient(new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15)
});

 

This solution works great in .NET projects like console apps, where dependency injection is not typically used.

 

So What About Azure?

 

Well… in Azure, Azure Functions rely entirely on dependency injection!
And luckily, in this context, Microsoft anticipated the problem.
Proper use of HttpClient is really easy here—thanks to…

 

IHttpClientFactory: The Right Way to Avoid Creating HttpClients

In Azure Functions, IHttpClientFactory is available by default, and you can start using it immediately!

Just inject it like any other service using dependency injection, and you’re good to go.

You can add it to any constructor and use it to obtain a client.
No more Port Exhaustion—the entire lifecycle of the handler is managed by the framework.

 

HttpClientFactory

HttpClientFactory in Azure Function

 

And by the way—you don’t even need to Dispose the client generated by the factory (no using block required).
If you look at the Dispose method of HttpClient… all it really tries to do is dispose of the handler—but only if told to!
And IHttpClientFactory tells clients not to do that.
You can even check the .NET source code (it’s open source) to see it for yourself.

 

Dispose HttpClient

 

IHttpClientFactory or Injected HttpClient?

You can also inject a HttpClient directly.
But… when should you use one or the other?

 

HttpClient Injection

 

It all depends on the lifetime of the service that’s calling it.
To put it simply: if the service is a Singleton, you must not inject a raw HttpClient.

Why? Because if you inject it into a Singleton, the client will never be refreshed.
Eventually, its handler will lose its DNS lease, and the factory won’t be able to give you a fresh one for that same client instance.

That’s why we prefer to use IHttpClientFactory to create a new client on-demand.
No risk of lease expiration.
For Transient and Scoped services, however, a new instance is created each time—so a new HttpClient as well. No expiration issue there.

To sum up:

  • Transient or Scoped: Use HttpClient
  • Singleton: Use IHttpClientFactory

 

By the way, Azure Functions are Transient. So if you need a client inside your Function, you can safely inject HttpClient directly.

This rule also applies to many other Microsoft libraries—for instance, the CosmosClient is meant to be Singleton.
Injecting it as Transient may lead to—you guessed it—Port Exhaustion.

 

Conclusion

 

HttpClient is incredibly useful—but also a potential source of problems if not used correctly.
Fortunately, the framework makes our life easier.

Using IHttpClientFactory inside Azure Functions protects you from all the typical issues associated with HttpClient.
Understanding how to use it properly—along with managing service lifetimes in your dependency injection setup—will ensure a stable, long-running, and reliable integration in your Azure Functions.