15% off your first purchase with code WELCOME15, up to 25 GB.

See pricing

Integration · HTTP clients

How to use a proxy with C# HttpClient

In .NET, a proxy is a WebProxy with a NetworkCredential, set on the handler behind your HttpClient: new SocketsHttpHandler { Proxy = new WebProxy("http://host:port") { Credentials = new NetworkCredential(user, pass) } }. The handler owns the connection pool, so the username, and with it the session and country, belongs to the handler.

Reviewed 30 Oct 2026

your proxy, as a URL
http://USERNAME_pool-arcfast_country-de:PASSWORD@proxy.arcproxies.io:8000

The short answer

  • Set Proxy on SocketsHttpHandler (or HttpClientHandler); credentials go on the WebProxy.
  • Microsoft's guidance: reuse clients, and use more than one instance when you need several proxies. One client per session or market.
  • A refused tunnel throws HttpRequestException with StatusCode 407 and HttpRequestError.ProxyTunnelError.
  • socks5:// in the WebProxy address works on .NET 6 and later.

Setup

Set up C# HttpClient with a proxy, step by step

  1. 01

    Create a client and check the exit IP

    PooledConnectionLifetime stops a long-lived client from holding one connection, and one exit, for ever. ConnectTimeout bounds connection setup; HttpClient.Timeout bounds the whole request.

    Program.cs
    using System.Net;static HttpClient ArcClient(string username) => new(new SocketsHttpHandler{    Proxy = new WebProxy("http://proxy.arcproxies.io:8000")    {        Credentials = new NetworkCredential(username, "PASSWORD"),    },    UseProxy = true,    ConnectTimeout = TimeSpan.FromSeconds(10),    PooledConnectionLifetime = TimeSpan.FromMinutes(2),}){    Timeout = TimeSpan.FromSeconds(30),};var client = ArcClient("USERNAME_pool-arcfast_country-de");Console.WriteLine(await client.GetStringAsync("https://api.ipify.org?format=json"));
  2. 02

    Keep a client per sticky session

    Mint a handle, build one client with the sticky username, and use it for every request in the task. A small dictionary keeps clients for handles you reuse.

    Program.cs, continued
    using System.Collections.Concurrent;using System.Security.Cryptography;var sessions = new ConcurrentDictionary<string, HttpClient>();HttpClient Session(string handle) => sessions.GetOrAdd(handle,    h => ArcClient($"USERNAME_pool-arcfast_country-de_session-{h}_sesstime-10"));var handle = Convert.ToHexString(RandomNumberGenerator.GetBytes(4)).ToLowerInvariant();for (var page = 1; page <= 3; page++){    using var res = await Session(handle).GetAsync($"https://example.com/search?q=lamp&page={page}");    Console.WriteLine($"{page} {(int)res.StatusCode}");}
  3. 03

    Target a country or city

    One client per market; the city is a token from the published list, lowercased without spaces. The examples use the residential pool, _pool-arcfast.

    Program.cs, continued
    var markets = new Dictionary<string, HttpClient>{    ["de-berlin"]     = ArcClient("USERNAME_pool-arcfast_country-de_city-berlin"),    ["gb-birmingham"] = ArcClient("USERNAME_pool-arcfast_country-gb_city-birmingham"),    ["nl-rotterdam"]  = ArcClient("USERNAME_pool-arcfast_country-nl_city-rotterdam"),};foreach (var (market, c) in markets){    using var res = await c.GetAsync("https://example.com/price/123");    Console.WriteLine($"{market} {(int)res.StatusCode}");}
  4. 04

    Separate 407s, timeouts and retryable responses

    On .NET 8 and later, HttpRequestError.ProxyTunnelError with status 407 means the tunnel was refused. A timeout surfaces as TaskCanceledException with an inner TimeoutException. Retry those, and 429 or 5xx, on a fresh session; handling 429 rate limits covers the backoff.

    Program.cs, continued
    async Task<HttpResponseMessage> FetchAsync(string url){    for (var attempt = 0; attempt < 4; attempt++)    {        var handle = Convert.ToHexString(RandomNumberGenerator.GetBytes(4)).ToLowerInvariant();        try        {            var res = await Session(handle).GetAsync(url);            if (res.StatusCode == HttpStatusCode.ProxyAuthenticationRequired)                throw new InvalidOperationException("proxy rejected the credentials");            if (res.StatusCode != HttpStatusCode.TooManyRequests && (int)res.StatusCode < 500)                return res;            res.Dispose();        }        catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.ProxyAuthenticationRequired)        {            throw new InvalidOperationException("proxy rejected the credentials", e);        }        catch (TaskCanceledException e) when (e.InnerException is TimeoutException) { }        catch (HttpRequestException) { }   // connection reset and similar        finally        {            if (sessions.TryRemove(handle, out var used)) used.Dispose();   // one-off session        }        await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));    }    throw new InvalidOperationException($"gave up on {url}");}
  5. 05

    Use SOCKS5 instead

    Since .NET 6, SocketsHttpHandler accepts socks5:// proxy addresses. The gateway takes the same credentials on the same port.

    Program.cs, continued
    var socks = new HttpClient(new SocketsHttpHandler{    Proxy = new WebProxy("socks5://proxy.arcproxies.io:8000")    {        Credentials = new NetworkCredential("USERNAME_pool-arcfast_country-de", "PASSWORD"),    },});Console.WriteLine(await socks.GetStringAsync("https://api.ipify.org"));

SocketsHttpHandler, HttpClientHandler and the default proxy

OptionHow the proxy is setNote
SocketsHttpHandlerProxy = new WebProxy(...)The implementation since .NET Core 2.1; exposes PooledConnectionLifetime and ConnectTimeout
HttpClientHandlerProxy = new WebProxy(...)Same proxy behaviour, fewer pool settings
HttpClient.DefaultProxyEnvironment variables or system settingsUsed when the handler sets no proxy; HTTPS_PROXY must be an http:// URL

Construct WebProxy with a full URI string. Microsoft notes that the host-only constructor overload cannot be used for SOCKS.

Why one handler means one exit

HttpClient reuses pooled connections, and an HTTPS connection through the gateway is one CONNECT tunnel with one exit. In our test, two requests on one client produced a single CONNECT. With a rotating username, an exit change therefore needs a new connection: after PooledConnectionLifetime, or from a different handler.

That is why the examples key clients by session handle. Microsoft's own guidelines say several instances are needed "for scenarios with multiple proxies"; what matters is not creating and disposing one per request. The rotating vs sticky sessions guide helps decide how long a session should live.

The problem

Common pitfalls

A new HttpClient per request

Every disposed handler leaves sockets in TIME_WAIT and every new one opens a new tunnel. Reuse clients per session or market.

Assuming the first CONNECT carries credentials

.NET sends the first CONNECT without credentials and answers the 407 challenge. That extra round trip is normal; a second 407 is the real failure.

IHttpClientFactory with sticky sessions

The factory recycles handlers on its own schedule. Fine for rotation, but a named client's handler, and its proxy username, is fixed when it is configured, so build session clients yourself.

Matching on exception text

Use HttpRequestException.StatusCode and HttpRequestError rather than parsing the message, which varies by version and language.

FAQ

Questions

Something not covered? Write to contact@arcproxies.io.

How do I use a proxy with authentication in C# HttpClient?

Create the client with a handler: new HttpClient(new SocketsHttpHandler { Proxy = new WebProxy("http://host:port") { Credentials = new NetworkCredential(user, pass) } }). The handler sends Basic proxy credentials in answer to the proxy's 407 challenge.

How do I use a different proxy per request in .NET?

A handler's proxy is fixed once it has sent a request, so use one HttpClient per proxy username and pick the client per request. Keep them in a dictionary keyed by session or market.

Does HttpClient support SOCKS5 proxies?

Yes, from .NET 6: use a socks5://host:port address in the WebProxy and set Credentials for username and password authentication.

How do I detect a 407 from the proxy in C#?

For HTTPS targets, catch HttpRequestException and check StatusCode == HttpStatusCode.ProxyAuthenticationRequired; on .NET 8+ HttpRequestError is ProxyTunnelError. For plain-HTTP targets the response itself has status 407.

Sources & review

Last reviewed by the ARC Proxies team. Prices are read from the live catalog at build time.

  1. [1]Microsoft Learn: Make HTTP requests with the HttpClient: Configure an HTTP proxy
  2. [2]Microsoft Learn: HttpClient guidelines for .NET
  3. [3]Microsoft .NET Blog: .NET 6 Networking Improvements (SOCKS proxy support)
  4. [4]Microsoft Learn: SocketsHttpHandler.Proxy Property

Next

What people build with it

Use caseProxies for web scraping and data collectionA second provider that just works — Scrapy, requests, Crawlee or Playwright, self-serve, no sales call, bandwidth that never expires.Use caseProxies for QA, app and localisation testingSee your own site the way a user in Tokyo, São Paulo or Munich sees it — currency, language, consent banner, CDN edge and all — before they report it.Use caseProxies for ad verification and ad testingConfirm your creatives render where you paid for them, in the geo you bought, and that they are not sitting next to something that damages the brand.GlossaryHTTP CONNECTHTTP CONNECT is the HTTP request method a client sends to a proxy to ask for a tunnel to a host and port; once the proxy answers with a 2xx status it blindly forwards bytes in both directions, which is how HTTPS passes through an HTTP proxy with TLS intact end to end (RFC 9110, section 9.3.6).GlossarySOCKS5SOCKS5 is a proxy protocol, standardised in RFC 1928 in 1996, that relays TCP connections and UDP datagrams between a client and a destination without interpreting the application traffic, with username/password authentication defined separately in RFC 1929.GuideRotating vs sticky sessionsRotating proxies change exit IP per connection; sticky sessions hold one IP for a set time. How each works, when to use which, and how to set them up.IntegrationJava HttpClientUse an authenticated proxy with java.net.http.HttpClient: ProxySelector, Authenticator, the Basic-over-CONNECT setting, sticky sessions and 407s.IntegrationGoRoute Go's net/http through an authenticated proxy: Transport.Proxy with url.UserPassword, a sticky session per request context, SOCKS5 and 407 checks.

Integrations

Other http clients

Python requestsUse an authenticated proxy with Python requests: the proxies argument, sticky sessions per task, geo-targeting, SOCKS5 and retries that skip 407 errors.HTTPXConfigure an authenticated proxy in HTTPX 0.28: the proxy= argument that replaced proxies=, mounts per domain, async clients, SOCKS5 and error handling.aiohttpUse an authenticated proxy with aiohttp: proxy= per request or per session, why proxy_auth is deprecated, SOCKS5 through aiohttp-socks, and 407 handling.AxiosUse an authenticated proxy with Axios in Node.js: the proxy option on 1.16.1+, https-proxy-agent for older versions, sticky sessions, SOCKS5 and 407s.Node.js fetchNode's built-in fetch has no proxy option. Use undici's ProxyAgent as the dispatcher, or NODE_USE_ENV_PROXY, with sticky sessions, SOCKS5 and 407 handling.curlSend curl through an authenticated proxy with -x and -U, check the exit IP, test sticky sessions and countries, use socks5h, and read 407 failures.GoRoute Go's net/http through an authenticated proxy: Transport.Proxy with url.UserPassword, a sticky session per request context, SOCKS5 and 407 checks.RubyUse an authenticated proxy in Ruby: Net::HTTP proxy arguments, Faraday proxy options, sticky sessions per task, geo-targeting and 407 handling.PHP GuzzleRoute PHP Guzzle and plain cURL through an authenticated proxy: the proxy option, sticky sessions, geo-targeting, retries that stop on a 407.Java HttpClientUse an authenticated proxy with java.net.http.HttpClient: ProxySelector, Authenticator, the Basic-over-CONNECT setting, sticky sessions and 407s.Rust reqwestRoute Rust reqwest through an authenticated proxy: Proxy::all with basic_auth, a Client per sticky session, socks5h, timeouts and 407 errors.wgetRun GNU Wget through an authenticated proxy: https_proxy, --proxy-user, a .wgetrc for credentials, sticky sessions for mirrors and exit codes.PostmanSend Postman requests through an authenticated proxy: the custom proxy settings, Proxy auth, checking the exit IP, sessions, countries and Newman.DockerGive Docker containers and builds an authenticated proxy: HTTP_PROXY, HTTPS_PROXY and NO_PROXY, build args, Compose, and why the daemon proxy differs.

Get started

Get credentials and paste them in.

Sign up, buy a gigabyte, generate a username in the dashboard. Code WELCOME15 takes 15% off your first purchase; bandwidth never expires.