Last week I spent an embarrassing amount of time staring at a 407 Proxy Authentication Required response, convinced my proxy provider was broken. Turns out I’d set credentials on the wrong property—a two-line fix that took me two hours to find. If that sounds familiar, this guide is for you.
Proxy configuration with HttpClient in C# is one of those topics where the basic pattern is straightforward, but the production gotchas—socket exhaustion, SOCKS5 version mismatches, credential confusion—eat up real time.
I’ve been working with web scraping and data extraction tools at Thunderbit for a while now, and I’ve seen these same mistakes come up again and again, both in our own engineering discussions and in the developer communities we follow. This walkthrough covers the full path: setup, authentication, proxy rotation, protocol selection, and a troubleshooting table that I genuinely wish had existed the day I started.
Difficulty: Beginner to Intermediate
Time Required: ~15 minutes to follow along, longer for production rotation patterns
What You’ll Need: .NET 6+ SDK (for SOCKS5 and modern handler features; .NET Framework 4.x works for basic HTTP proxy examples), a code editor, and at least one proxy endpoint to test with
What Is HttpClient and Why Does It Need a Proxy?

HttpClient is the built-in .NET class in System.Net.Http for sending HTTP requests and receiving responses. It supports async/await, custom headers, cancellation tokens, and handler-based configuration. Microsoft describes it as a class for sending HTTP requests and receiving HTTP responses from a resource identified by a URI.
A proxy server is an intermediary that sits between your application and the target website. When you route traffic through a proxy, the target sees the proxy’s IP address instead of yours.
HttpClient itself doesn’t have a Proxy property. Proxy routing is configured on the underlying handler—either HttpClientHandler or SocketsHttpHandler—which accepts a WebProxy instance. The mental model looks like this:
[Your C# App] → [HttpClient + Handler] → [Proxy Server] → [Target Website]
That’s why “change the proxy on a live HttpClient” is a design problem, not a simple property assignment. More on that in the rotation section.
Try Thunderbit for easier data extraction
Why Use a Proxy With HttpClient in C#
Developers route HttpClient traffic through proxies for a handful of recurring reasons, and the right proxy type depends on the job.
- Avoid IP bans and rate limits: Essential for web scraping, lead generation, or price monitoring at scale. A single IP hammering a site will get blocked quickly.
- Bypass geo-restrictions: Access region-locked APIs or content by routing through proxies in specific countries.
- Hide your origin IP: Add a layer of privacy for sensitive data collection or competitive research.
- Corporate or compliance requirements: Many enterprises require outbound traffic to pass through a centralized gateway for logging and governance.
- Testing and QA: Simulate requests from different locations or network conditions without physically deploying infrastructure in those regions.
| Use Case | Typical Proxy Choice | Why It Fits |
|---|---|---|
| Web scraping at scale | Rotating residential proxies | More IP diversity, harder for anti-bot systems to classify |
| E-commerce price monitoring | Residential or geo-targeted datacenter | Region-specific pricing and inventory checks |
| API access through a fixed gateway | Datacenter proxy or corporate proxy | Predictable IP allowlisting, lower cost |
| Enterprise compliance | System proxy, PAC proxy, authenticated company proxy | Centralized logging and outbound control |
| QA and localization testing | Country-specific proxy pool | Simulates real user access from target regions |
Proxy usage also scales in predictable stages. You start with a single static proxy to confirm routing. A production scraper moves to a pool, mapping requests to proxies by target domain, geography, or failure rate. Mature teams often shift to a managed proxy gateway where rotation, retries, and session affinity are handled behind one endpoint.
Proxy rotation is not a silver bullet. If a target blocks suspicious behavior, rotating IPs helps only when request cadence, headers, cookies, and TLS fingerprinting are also handled carefully.
Which .NET Version Supports What: A Quick Compatibility Matrix
Copying a proxy snippet from a blog post into the wrong target framework is a major source of silent failures. The biggest dividing line is .NET Framework 4.x versus modern .NET (.NET 6+). Here’s what works where:

| Capability | .NET Framework 4.x | .NET 6 | .NET 7 | .NET 8–9 |
|---|---|---|---|---|
WebProxy + HttpClientHandler | Yes | Yes | Yes | Yes |
SOCKS5 via WebProxy("socks5://...") | No | Yes (added in .NET 6) | Yes | Yes |
SocketsHttpHandler (default handler) | No | Yes | Yes | Yes |
HttpClient.DefaultProxy static | No | Yes | Yes | Yes |
PooledConnectionLifetime | No | Yes | Yes | Yes |
If you’re targeting .NET Framework 4.x, stick to HTTP/HTTPS proxies with HttpClientHandler and WebProxy. SOCKS5 and modern pooling controls require .NET 6 or later.
One subtle behavior to watch for: HttpClient.DefaultProxy is a static property in modern .NET. If it’s set in shared startup code or inherited from environment variables like HTTPS_PROXY or HTTP_PROXY, every HttpClient instance picks it up unless you explicitly override the handler. In containerized deployments, this is a common source of “why is my client using a proxy I never configured?” confusion.
Step 1: Create a New C# Console Project
Open a terminal and scaffold a new project:
dotnet new console -n ProxyHttpClientDemo
cd ProxyHttpClientDemo
Confirm your SDK version with dotnet --version. The examples in this guide target .NET 6+ for full feature coverage. If you need the latest LTS SDK, grab it from Microsoft’s download page.
Open Program.cs in your editor. That’s where all the action happens.
Step 2: Make a Baseline HTTP Request (No Proxy)
Before configuring a proxy, establish your real egress IP. This way, once the proxy is active, you can confirm the IP actually changed.
using System.Net.Http;
using var client = new HttpClient();
var ip = await client.GetStringAsync("https://api.ipify.org/");
Console.WriteLine($"Direct IP: {ip}");
Run it. You should see your current public IP address, something like:
Direct IP: 203.0.113.10
Save that value mentally. After the next step, it should be different.
Step 3: Configure a WebProxy With HttpClientHandler
The canonical pattern is three objects: a WebProxy, a handler, and the client.
using System.Net;
using System.Net.Http;
var proxy = new WebProxy("http://proxy.example.com:8080")
{
BypassProxyOnLocal = false
};
var handler = new HttpClientHandler
{
Proxy = proxy,
UseProxy = true,
UseDefaultCredentials = false
};
using var client = new HttpClient(handler);
var ip = await client.GetStringAsync("https://api.ipify.org/");
Console.WriteLine($"Proxy IP: {ip}");
Replace proxy.example.com:8080 with your actual proxy endpoint. If everything is wired correctly, the output IP should now match your proxy’s exit IP—not your real one.
Key properties to understand:
Proxy— theIWebProxyinstance the handler uses for routing.UseProxy = true— tells the handler to actually use the configured proxy. (Sounds obvious, but forgetting this is a real debugging time sink.)BypassProxyOnLocal = false— prevents the handler from skipping the proxy for destinations that look “local.”UseDefaultCredentials— controls whether the handler sends Windows default credentials. This is not the same as proxy username/password.

Step 4: Add Proxy Authentication With NetworkCredential
Most paid proxy providers require credentials. The correct pattern is to set them on the WebProxy object itself:
using System.Net;
using System.Net.Http;
var proxy = new WebProxy("http://proxy.example.com:8080")
{
Credentials = new NetworkCredential("proxy-user", "proxy-password")
};
var handler = new HttpClientHandler
{
Proxy = proxy,
UseProxy = true
};
using var client = new HttpClient(handler);
var ip = await client.GetStringAsync("https://api.ipify.org/");
Console.WriteLine($"Authenticated proxy IP: {ip}");
Many providers give you a URL format like http://username:password@host:port. For .NET code, prefer NetworkCredential over embedding credentials in the URI string. It avoids escaping problems with special characters in passwords and keeps the distinction between URI and credentials explicit.
I’ll cover the most common authentication mistake—and why it causes 407 errors—in a dedicated section below.
Step 5: Export or Use the Response Data
For anything beyond a quick IP check, handle the response properly:
using var response = await client.GetAsync("https://example.com/api/products");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"Request failed: {(int)response.StatusCode} {response.ReasonPhrase}");
return;
}
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
For scraping workflows, the proxied request is only the transport layer. You still need parsing, normalization, deduplication, retries, and exports to Excel, Google Sheets, databases, or other destinations. Tools like Thunderbit can automate the extraction and export steps—its Chrome extension handles structured data extraction and free exports to Google Sheets, Excel, Airtable, or Notion without requiring you to write parsing code.
Export scraped data to Excel, Sheets, Airtable, or Notion Get Started Free
Proxy Credentials vs. Server Credentials: The Mistake That Causes 407 Errors

I’ve seen this mistake in Stack Overflow threads, Microsoft Q&A posts, and—full disclosure—my own code.
The distinction is simple but easy to confuse:
- Proxy credentials authenticate you to the proxy server itself.
- Server credentials authenticate you to the destination/target server.
In HttpClientHandler, these live on different properties. Setting credentials on the wrong one is the #1 cause of 407 Proxy Authentication Required errors.
// ❌ WRONG — sets credentials for the destination server, not the proxy
handler.Credentials = new NetworkCredential("user", "pass");
// ✅ CORRECT — sets credentials on the proxy object itself
handler.Proxy = new WebProxy("http://proxy:8080")
{
Credentials = new NetworkCredential("user", "pass")
};
HttpClientHandler.Credentials targets the destination. WebProxy.Credentials targets the proxy. If the proxy returns 407, your credentials belong on the proxy.
One more trap: HttpClientHandler.PreAuthenticate controls pre-authentication behavior for target server authentication. It does not control the Proxy-Authorization header. Don’t use it as a 407 fix.
How to Rotate Proxies With HttpClient in C#
Developers ask this constantly in forums. The answer is initially disappointing: you cannot change the proxy on a live HttpClient instance. The proxy lives on the handler. The handler is set at construction time. HttpClient exposes no mutable Proxy property.
The naive workaround—new HttpClient(new HttpClientHandler { Proxy = ... }) for every request—creates a different problem. Microsoft explicitly warns that creating and disposing clients per request can exhaust available TCP ports because ports are not released immediately after connection closure.

So here are three production-grade patterns that actually work.
Option 1: Named Clients via IHttpClientFactory
If your proxy set is known at startup, named clients are the lowest-complexity option. Each named client gets its own handler configuration, and application code resolves by name at runtime.
builder.Services.AddHttpClient("proxy-us")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
Proxy = new WebProxy("http://us-proxy.example.com:8080")
{
Credentials = new NetworkCredential("user", "pass")
},
UseProxy = true
});
builder.Services.AddHttpClient("proxy-eu")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
Proxy = new WebProxy("http://eu-proxy.example.com:8080")
{
Credentials = new NetworkCredential("user", "pass")
},
UseProxy = true
});
// At request time:
var client = httpClientFactory.CreateClient("proxy-us");
The factory manages handler lifetimes and avoids the per-request client anti-pattern.
Option 2: SocketsHttpHandler + PooledConnectionLifetime (.NET 6+)
For a long-lived client behind a proxy gateway that rotates exit IPs on new connections, PooledConnectionLifetime forces connections to be recreated after a configured duration.
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("http://rotating-gateway.example.com:8080")
{
Credentials = new NetworkCredential("user", "pass")
},
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(5)
};
using var client = new HttpClient(handler);
This doesn’t magically change the Proxy object per request. It works best with proxy gateways that assign a different exit IP on each new TCP connection, or with DNS-backed proxy pools where the hostname resolves to different endpoints over time.
Option 3: Custom DelegatingHandler for Advanced Proxy Selection
When proxy selection depends on request URL, payload, or runtime context, a custom routing handler can inspect each request and forward it to the correct inner handler pipeline.
public sealed class ProxyRoutingHandler : DelegatingHandler
{
private readonly IReadOnlyDictionary<string, HttpMessageInvoker> _clients;
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var key = SelectProxyKey(request);
return _clients[key].SendAsync(request, cancellationToken);
}
}
This is an advanced design. Thread safety, disposal, handler reuse, retry behavior, and logging all become your responsibility. I’d recommend it only when the first two options genuinely don’t fit.
Comparing the Three Approaches
| Approach | Complexity | .NET Version | Thread Safety | Overhead |
|---|---|---|---|---|
| Named clients (IHttpClientFactory) | Low | .NET Core 2.1+ | High (immutable config) | Low |
| SocketsHttpHandler + PooledConnectionLifetime | Medium | .NET 6+ | High | Low |
| Custom DelegatingHandler | High | Any | Depends on implementation | Medium |
For most teams, named clients are the right starting point. Move to PooledConnectionLifetime for stable rotating gateways, and custom routing only when proxy choice depends on request-level metadata.
Picking the Right Proxy Protocol: HTTP, HTTPS, and SOCKS5
Not all proxies speak the same language, and using the wrong protocol scheme will give you confusing errors.
HTTP proxy: Understands HTTP requests. For plain HTTP targets, it can forward requests directly. For HTTPS targets, the client sends a CONNECT request to create a tunnel, then TLS is negotiated through that tunnel with the destination. This is the most common model.
HTTPS-terminating proxy: The proxy presents its own TLS certificate and re-encrypts upstream traffic. Common in enterprise inspection systems and some managed scraping APIs. Can trigger certificate validation errors if the client doesn’t trust the proxy’s certificate chain.
SOCKS5 proxy: A transport-layer TCP tunnel that works for any TCP traffic, not just HTTP. Widely used by residential proxy providers. Natively supported in .NET 6+.
SOCKS5 example:
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy("socks5://proxy.example.com:1080")
{
Credentials = new NetworkCredential("proxy-user", "proxy-password")
},
UseProxy = true
};
using var client = new HttpClient(handler);
var ip = await client.GetStringAsync("https://api.ipify.org/");
Console.WriteLine(ip);
A Note on SSL Certificate Validation
When using HTTPS-terminating proxies, you may see RemoteCertificateNameMismatch errors. The ServerCertificateCustomValidationCallback can customize validation:
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
Use this only in local development or with a trusted, approved TLS-intercepting proxy. Blindly returning true disables a critical security check and opens you to man-in-the-middle attacks. In production with standard CONNECT or SOCKS proxies, keep SSL validation enabled.
Troubleshooting Common Proxy Errors in C# HttpClient

This table maps the visible symptom to the likely root cause and the first fix to try. I’d bookmark this section—it covers the errors that show up most often in Stack Overflow threads and developer forums.
| Error / Symptom | Common Cause | Fix |
|---|---|---|
| 407 Proxy Authentication Required | Credentials set on handler.Credentials instead of handler.Proxy.Credentials; wrong username format; special characters in URL-embedded password | Use WebProxy.Credentials = new NetworkCredential(...); avoid embedding credentials in URI; verify provider username format |
| TaskCanceledException / Timeout | Proxy endpoint slow, unreachable, overloaded, or blocked by firewall; default 100s timeout too short | Test proxy with curl; increase HttpClient.Timeout only after proving endpoint works; add retries and proxy health checks |
| SocketException / Socket exhaustion | Creating and disposing HttpClient or handlers per request | Use IHttpClientFactory, singleton clients, or SocketsHttpHandler with pooling controls |
| SSL RemoteCertificateNameMismatch | HTTPS interception by corporate or managed proxy | Install/trust proxy CA where appropriate; use custom validation only in controlled dev or approved MITM scenarios |
| 302 Redirect loop | Corporate proxy/VPN captive page or allowlist block redirecting repeatedly | Test direct connection; inspect Location headers; check proxy allowlist and authentication portal |
| HttpRequestException / No connection with SOCKS URL | Running SOCKS code on .NET Framework or older .NET; wrong scheme or port | Use .NET 6+ for native SOCKS support; verify socks5://host:port; test with provider docs |
| Proxy appears ignored | UseProxy = false; target bypassed as local; NO_PROXY environment variable; handler configured differently than expected | Set UseProxy = true; inspect HttpClient.DefaultProxy; clear or override environment variables; set BypassProxyOnLocal = false |
Quick Debugging Flow
- Did the request succeed? → Yes: compare
api.ipify.orgoutput with expected proxy IP. - No, there’s an HTTP status code? → 407: fix proxy credentials. 403/429: target blocked or rate-limited the proxy. 3xx loop: proxy/corporate gateway may be redirecting.
- No status code, just an exception? → Timeout: test proxy reachability. Socket/certificate exception: check pooling, protocol, TLS, and .NET version.
Useful first commands to validate the proxy itself outside of .NET:
curl -x http://user:pass@proxy.example.com:8080 https://api.ipify.org/
curl --socks5 user:pass@proxy.example.com:1080 https://api.ipify.org/
If curl works but your C# code doesn’t, the difference is usually auth scheme, TLS trust store, environment variables, or credential escaping. Match curl’s proxy URL, scheme, and auth exactly, then move credentials into NetworkCredential.
When to Skip Proxy Management: The No-Code Alternative
A meaningful share of developers searching for “HttpClient proxy C#” are not trying to learn proxy theory—they’re trying to keep a scraper running. It’s worth being honest about when custom C# proxy code is the right tool and when it isn’t.
Build a custom C# scraper with proxy rotation when:
- You need full control over request logic, cookies, headers, retries, and parsing
- The scraper integrates into an existing .NET codebase or internal service
- Compliance or security requirements demand you own the infrastructure end-to-end
Use a no-code tool like Thunderbit when:
- The goal is structured data extraction from websites, not HTTP infrastructure
- You’d rather not maintain proxy pools, handle CAPTCHAs, or debug socket exhaustion
- The team needs data in Excel, Google Sheets, Airtable, or Notion without writing parsing code
Thunderbit’s Chrome extension handles proxy rotation and anti-bot measures automatically through its cloud scraping option. Its API lets developers define a JSON schema and get structured data back without managing HttpClient or WebProxy at all. For teams doing web scraping for price comparison or lead extraction, the setup time difference is significant.
| Scenario | Custom C# + Proxy | Thunderbit |
|---|---|---|
| Full control over request logic | Yes | No (API-level control) |
| Proxy management required | Yes | No (handled automatically) |
| Anti-bot / CAPTCHA handling | Manual or third-party | Built-in |
| Setup time | Hours to days | Minutes |
| Best for | Existing .NET codebases, custom pipelines | Quick data extraction, non-technical teams, spreadsheet exports |
This isn’t a “never use HttpClient” argument. If you’re building a production .NET service, you absolutely should understand proxy configuration. But if you’re spending hours debugging 407 errors for a one-off data collection job, simpler options exist—and there’s no shame in using them. You can explore Thunderbit’s pricing or check out the YouTube channel for walkthroughs.
Key Takeaways
The core pattern stays the same: WebProxy → handler → HttpClient. Everything after that is about dodging the operational mistakes that surface in production.
- Credentials go on the proxy, not the handler. The side-by-side snippet in the 407 section is the single most important thing to remember.
- Don’t create a new
HttpClientper request or per proxy. UseIHttpClientFactoryfor named clients,SocketsHttpHandlerwithPooledConnectionLifetimefor rotating gateways, or a custom routing handler for advanced scenarios. - Check your .NET version before copying SOCKS5 or
SocketsHttpHandlercode. The compatibility matrix above saves you from silent failures. - Test the proxy outside of .NET first. A quick
curlcommand eliminates a whole category of debugging. - For structured data extraction without proxy headaches, tools like Thunderbit handle the transport layer so you can focus on the data itself.
Next time you hit a 407 or a TaskCanceledException, start with the troubleshooting table above.
FAQs
Can I change the proxy on an existing HttpClient instance?
No. The proxy is bound to the handler, and the handler is set at construction time. HttpClient does not expose a mutable Proxy property. For different proxies, create separate handlers and clients, and manage them with IHttpClientFactory named clients or a pool of preconfigured clients.
Does HttpClient use the system proxy by default?
Yes. In modern .NET, if you don’t explicitly set a handler, HttpClient inherits the system’s default proxy settings—including environment variables like HTTPS_PROXY and HTTP_PROXY via HttpClient.DefaultProxy. To opt out, explicitly set UseProxy = false on the handler.
How do I use a SOCKS5 proxy with HttpClient in C#?
Use new WebProxy("socks5://host:port") with SocketsHttpHandler. Native SOCKS proxy support requires .NET 6 or later. On .NET Framework 4.x, SOCKS5 is not natively supported—you’d need a third-party library.
Why do I keep getting 407 Proxy Authentication Required?
Most likely, you’re setting credentials on handler.Credentials (which targets the destination server) instead of handler.Proxy.Credentials (which targets the proxy). See the “Proxy Credentials vs. Server Credentials” section above for the correct pattern.
Is it safe to disable SSL certificate validation when using a proxy?
Only in local development or when you trust the proxy provider completely (for example, a managed scraping API in HTTPS proxy mode). In production with standard CONNECT or SOCKS proxies, keep SSL validation enabled to prevent man-in-the-middle attacks.
Try Thunderbit for effortless web scraping Get Started Free
Learn More


