C# HttpClient proxy: 407 with the right login

WebProxy accepts an address with user:pass@ in it and then doesn't send that login. The proxy answers 407 and the password looks wrong when it isn't.

5 min readTested on .NET 10 and .NET 8
A WebProxy with the login inside its address gets 407 from the proxy; the same login set as WebProxy.Credentials returns 200.Same login, two places to put itnew WebProxy("http://USER:PASS@via.proxies.vision:9000")the address keeps its login, the request doesn't send itHttpRequestException: … failed with status code '407'new WebProxy("http://via.proxies.vision:9000"){ Credentials = new NetworkCredential(user, pass) }200 { "country": "DE", … }.NET 10 and 8 · October 20, 2026

In short

Put the login on the proxy object, not into its address: new WebProxy("http://via.proxies.vision:9000") with Credentials = new NetworkCredential(user, pass). HttpClientHandler.Credentials is the website’s login and doesn’t reach the proxy either.

WebProxy.CredentialsHttpRequestError.ProxyTunnelError

For an https:// URL, HttpClient asks the proxy to open a tunnel with CONNECT. A proxy that needs a login answers 407 Proxy Authentication Required, and HttpClient tries again with whatever credentials it has for the proxy. If it has none, the 407 is final:

txt
System.Net.Http.HttpRequestException: The proxy tunnel request to proxy 'http://via.proxies.vision:9000/' failed with status code '407'.

The credentials for the proxy come from exactly one place: WebProxy.Credentials (or HttpClientHandler.DefaultProxyCredentials). Two lookalikes don’t count. A user:pass@ written into the proxy address is kept in the address and ignored for authentication. And HttpClientHandler.Credentials answers a website that asks for a login, not the proxy. We ran each variant with the same, correct login:

407new WebProxy("http://USER:PASS@via.proxies.vision:9000")login in the address is ignored
407HttpClientHandler { Proxy = …, Credentials = … }that's the website's login
200new WebProxy("http://via.proxies.vision:9000") { Credentials = … }the proxy's login
200HttpClientHandler { DefaultProxyCredentials = … }also the proxy's

The environment variable is the exception. A plain new HttpClient() on Linux or macOS reads HTTPS_PROXY, and there a login inside the URL is used: HTTPS_PROXY=http://USER:PASS@via.proxies.vision:9000 returned 200 in our test. So code that worked from the environment can break the moment the same string moves into a WebProxy.

Every message, reproduced

HttpClient on .NET 10.0.13 against via.proxies.vision. .NET 8 printed the same 407 message.

What we setResult
Correct login on WebProxy.Credentials200
No credentials, or the login only in the addressHttpRequestException: The proxy tunnel request … failed with status code '407'.
Wrong password… failed with status code '407'.
Wrong password, http:// URLno exception: StatusCode 407, the proxy's error page as the body
Username -country-usa… failed with status code '403'.
Chicago with country de… failed with status code '465'.
socks5://…:11000200
socks5h://…:11000NotSupportedException: Only the 'http', 'https', 'socks4', 'socks4a' and 'socks5' schemes are allowed for proxies.
socks5://, wrong passwordHttpRequestException … inner SocksException: Failed to authenticate with the SOCKS server.
https:// as the proxy schemeThe SSL connection could not be established … Cannot determine the frame size or a corrupted frame was received.
http:// to the SOCKS portHttpIOException: The response ended prematurely. (ResponseEnded)
UseProxy = false200, from our own IP
Fig. 1 — https://ipinfo.io through our gateway, October 20, 2026. 403 and 465 are our gateway’s statuses; see below.

403: a value in the username

Targeting goes into the proxy username. Our gateway refuses values it can’t read, such as a three-letter country code or a session label on the rotating port. A key it doesn’t know is ignored without an error.

465: no exit

The login and the username are fine, but no residential exit matches every filter right now. Drop the narrowest one, usually the city. The same three codes in other clients: Java, curl.

Tell proxy errors apart in code

Since .NET 8 a refused tunnel carries HttpRequestError.ProxyTunnelError, and for a 407 e.StatusCode is ProxyAuthenticationRequired. Catch on that instead of parsing the message. For http:// URLs there is no tunnel: the proxy’s 407 comes back as an ordinary response, so check StatusCode before you read the body.

SOCKS5 without the h

.NET accepts socks5:// and rejects socks5h://. That isn’t a gap: with a hostname that doesn’t exist, .NET’s socks5 got SOCKS server failed to connect to the destination. Received error code 0x01, so the name went to the proxy and was resolved there.

On our port 9000 every new connection gets a new exit. HttpClient, though, keeps its connection to the proxy open and sends the next request to the same host through the same tunnel. We made four requests with one client, then three with a new client each:

1 IPone HttpClient, 4 requeststhe tunnel is reused
3 IPsa new HttpClient per request, 3 requestsa new connection each

Choose on purpose

Don’t create a client per request to force rotation in a busy app; that exhausts sockets. If you need one IP for a while, put a session into the username and use port 10000: USER-country-de-session-job1-lifetime-10. If you need a new IP per request, a short PooledConnectionLifetime on SocketsHttpHandler limits how long a tunnel is reused: with 2 seconds, four requests three seconds apart came from four IPs.

A setup that works

A .NET console app (dotnet new console), Program.cs replaced with this. Ran unchanged with dotnet run on .NET 10 and 8.

Program.cs
using System.Net;

var user = Environment.GetEnvironmentVariable("PROXY_USER") + "-country-de";
var pass = Environment.GetEnvironmentVariable("PROXY_PASS");

var handler = new SocketsHttpHandler
{
    // The login belongs on the WebProxy. A user:pass@ inside the proxy address is ignored.
    Proxy = new WebProxy("http://via.proxies.vision:9000")
    {
        Credentials = new NetworkCredential(user, pass),
    },
    UseProxy = true,
};
using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) };

try
{
    Console.WriteLine(await client.GetStringAsync("https://ipinfo.io/json"));
}
catch (HttpRequestException e) when (e.HttpRequestError == HttpRequestError.ProxyTunnelError)
{
    // 407: the login. 403: a value in the username the gateway can't read. 465: no exit matches.
    Console.Error.WriteLine(e.Message);
    Environment.Exit(1);
}
txt
{
  "ip": "217.252.154.93",
  "city": "Aschaffenburg",
  "region": "Bavaria",
  "country": "DE",
  …

For SOCKS5, change the address to socks5://via.proxies.vision:11000 and keep the credentials. The same login works on both ports.

Why does HttpClient get 407 from the proxy when the login is correct?
Usually because the username and password are written into the proxy address, as in new WebProxy("http://user:pass@host:port"). WebProxy keeps the address but doesn't send that login. Set WebProxy.Credentials = new NetworkCredential(user, pass) instead. The other common cause is HttpClientHandler.Credentials, which is the login for the website, not for the proxy.
How do I set proxy credentials for HttpClient in C#?
Create a WebProxy for the proxy address, set its Credentials to a NetworkCredential with your username and password, and pass it to HttpClientHandler.Proxy or SocketsHttpHandler.Proxy. HttpClientHandler.DefaultProxyCredentials works too.
Does HttpClient read HTTPS_PROXY?
Yes. A plain new HttpClient() uses the HTTPS_PROXY and HTTP_PROXY environment variables on Linux and macOS, and a login written into that variable is sent. In our test HTTPS_PROXY=http://user:pass@host:9000 worked where the same address in new WebProxy() did not.
Does HttpClient support SOCKS5 proxies?
Yes, since .NET 6: new WebProxy("socks5://host:port") with Credentials. socks5h:// is rejected with NotSupportedException; it isn't needed, because .NET's socks5 already lets the proxy resolve the hostname.
Why do all my requests through a rotating proxy use the same IP?
HttpClient keeps the connection to the proxy open and sends the next request through the same tunnel, so the exit doesn't change. A new HttpClient (or a new connection) gets a new exit on a rotating port. For a fixed IP on purpose, use a sticky session in the username instead of relying on connection reuse.

Residential proxies, pay per GB

HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.