A C# HttpClient proxy is set on the handler, not the client: create a WebProxy for the proxy URI, put a NetworkCredential with your username and password in its Credentials, and assign it to HttpClientHandler.Proxy or SocketsHttpHandler.Proxy. .NET opens a CONNECT tunnel for HTTPS sites and answers the proxy's 407 challenge with those credentials. SOCKS5 works the same way with a socks5:// URI.

Every snippet on this page ran on 2026-09-30 with the .NET 8 SDK (8.0.425) and Microsoft.Extensions.Http 8.0.1 on Linux, against local authenticating HTTP proxies and a SOCKS5 proxy that enforces username and password and logs each connection.

Before you start: copy your proxy details

  1. Open your order in the dashboard at https://app.proxyhive.io.
  2. Copy HOST, PORT, USERNAME and PASSWORD. Each ISP or datacenter IP is its own endpoint, and the order lists separate HTTP, HTTPS and SOCKS5 ports for every IP. Use the HTTP port with an http:// proxy URI.
  3. Keep them in the environment or in user secrets, never in source:
export PROXY_HOST=HOST PROXY_PORT=PORT PROXY_USER=USERNAME PROXY_PASS=PASSWORD

If you still need an address, static ISP proxies can be bought as a single IP.

C# HttpClient proxy with WebProxy and credentials

using System.Net;

var proxy = new WebProxy($"http://{Environment.GetEnvironmentVariable("PROXY_HOST")}:{Environment.GetEnvironmentVariable("PROXY_PORT")}")
{
    Credentials = new NetworkCredential(
        Environment.GetEnvironmentVariable("PROXY_USER"),
        Environment.GetEnvironmentVariable("PROXY_PASS")),
};

var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true };
using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) };

Console.WriteLine(await client.GetStringAsync("https://api.ipify.org?format=json"));

UseProxy is already true by default; setting it to false makes the handler ignore Proxy entirely, which is a quick way to compare direct and proxied responses. The http:// scheme describes how .NET talks to the proxy; https:// targets still get end-to-end TLS through the tunnel.

Our proxy log showed .NET sending the first CONNECT without credentials, receiving the 407, then retrying with them. That is one extra round trip per new connection, and a good reason to reuse the client.

SocketsHttpHandler: timeouts and connection lifetime

On .NET Core and later, HttpClientHandler wraps SocketsHttpHandler. Use the inner handler directly when you want its knobs:

using System.Net;

string Env(string name) => Environment.GetEnvironmentVariable(name)!;

var handler = new SocketsHttpHandler
{
    Proxy = new WebProxy($"socks5://{Env("PROXY_HOST")}:{Env("SOCKS5_PORT")}")
    {
        Credentials = new NetworkCredential(Env("PROXY_USER"), Env("PROXY_PASS")),
    },
    ConnectTimeout = TimeSpan.FromSeconds(10),
    PooledConnectionLifetime = TimeSpan.FromMinutes(5),
};
using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) };

Console.WriteLine(await client.GetStringAsync("https://api.ipify.org?format=json"));

ConnectTimeout caps the connection to the proxy, HttpClient.Timeout caps the whole request, and PooledConnectionLifetime recycles pooled connections so a long-lived client does not hold one tunnel forever. For an HTTP proxy, change the URI to http:// and the port to PROXY_PORT.

.NET SOCKS5 proxy

The snippet above is the SOCKS5 setup: .NET 6 added socks4, socks4a and socks5 URIs to SocketsHttpHandler, with username and password taken from the NetworkCredential. Our SOCKS server logged a domain name for the target, so .NET lets the proxy resolve DNS. With a wrong password the call fails with HttpRequestException: An error occurred while establishing a connection to the proxy tunnel, wrapping SocksException: Failed to authenticate with the SOCKS server. When SOCKS5 beats HTTP is covered in HTTP vs SOCKS5 proxies.

Proxy environment variables: HTTPS_PROXY and ALL_PROXY

A plain new HttpClient() uses HttpClient.DefaultProxy, which on Linux and macOS reads HTTP_PROXY, HTTPS_PROXY, ALL_PROXY and NO_PROXY, lowercase first. Credentials and SOCKS both work in the URL; we ran ALL_PROXY=socks5://user:pass@... and HTTPS_PROXY=http://user:pass@... successfully. The DefaultProxy documentation lists the rules, including how NO_PROXY matches subdomains.

One caution from our run: when the proxy came from HTTPS_PROXY and the password was wrong, the exception message included the full proxy URL, password and all: The proxy tunnel request to proxy 'http://user:...@HOST:PORT/' failed with status code '407'. If your logs capture exception messages, prefer WebProxy with NetworkCredential, whose error names only the host and port.

IHttpClientFactory with a proxy

In ASP.NET Core or any app using dependency injection, configure the primary handler once on a named or typed client:

using System.Net;
using Microsoft.Extensions.DependencyInjection;

string Env(string name) => Environment.GetEnvironmentVariable(name)!;

var services = new ServiceCollection();
services.AddHttpClient("proxied", client => client.Timeout = TimeSpan.FromSeconds(30))
    .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
    {
        Proxy = new WebProxy($"http://{Env("PROXY_HOST")}:{Env("PROXY_PORT")}")
        {
            Credentials = new NetworkCredential(Env("PROXY_USER"), Env("PROXY_PASS")),
        },
    });

var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();
var client = factory.CreateClient("proxied");
Console.WriteLine(await client.GetStringAsync("https://api.ipify.org?format=json"));

The factory pools and recycles handlers for you, which avoids both socket exhaustion from new HttpClient() per request and stale DNS from one client kept forever. In a web app, the services.AddHttpClient(...) line goes in Program.cs on builder.Services.

Rotate across several static IPs

A handler's proxy is fixed after the first request, so rotation means one client per endpoint:

using System.Net;

HttpClient ClientFor(Uri proxy)
{
    var userPass = proxy.UserInfo.Split(':', 2);
    var handler = new SocketsHttpHandler
    {
        Proxy = new WebProxy($"{proxy.Scheme}://{proxy.Host}:{proxy.Port}")
        {
            Credentials = new NetworkCredential(
                Uri.UnescapeDataString(userPass[0]), Uri.UnescapeDataString(userPass[1])),
        },
        ConnectTimeout = TimeSpan.FromSeconds(10),
        PooledConnectionLifetime = TimeSpan.FromMinutes(5),
    };
    return new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) };
}

var clients = Environment.GetEnvironmentVariable("PROXY_URLS")!
    .Split(',')
    .Select(url => ClientFor(new Uri(url)))
    .ToArray();
var next = 0;

for (var i = 0; i < 4; i++)
{
    var client = clients[Interlocked.Increment(ref next) % clients.Length];
    Console.WriteLine(await client.GetStringAsync("https://api.ipify.org?format=json"));
}

PROXY_URLS holds comma-separated http://USER:PASS@HOST:PORT entries (or socks5://). Four requests over two proxies gave one authenticated tunnel per proxy, each reused. Because ISP and datacenter IPs are static for the term, the order you cycle them in is the only rotation there is.

Common errors

ExceptionCauseFix
The proxy tunnel request to proxy ... failed with status code '407'Wrong credentialsRe-copy them from the order
SocksException: Failed to authenticate with the SOCKS serverWrong SOCKS credentialsSame
HttpRequestException: Connection refused (HOST:PORT)Wrong host or portRe-copy them; SOCKS5 and HTTP have different ports
TaskCanceledException after 30 sHttpClient.Timeout reachedCheck the proxy with cURL first

Status codes the target returns through a working tunnel are covered in proxy error codes.

Next steps