Skip to the content.

curl proxy in C# (curl -x and SOCKS5)

To run curl through a proxy in C#, add -x (or --proxy) with the proxy URL to the command you pass to Curl.ExecuteAsync. -U / --proxy-user adds proxy credentials, --noproxy lists hosts to reach directly, --socks5 routes the request through a SOCKS5 proxy, and the http_proxy/https_proxy/all_proxy/NO_PROXY environment variables are honoured exactly as curl honours them.

HTTP proxy

using CurlDotNet;

var result = await Curl.ExecuteAsync(
    "curl -x http://proxy.example.com:8080 https://httpbin.org/ip");

Console.WriteLine(result.Body);   // {"origin": "<proxy's address>"}

If the proxy URL has no scheme, http:// is assumed, and if it has no port, curl’s default proxy port 1080 is used (so write the port, :8080 or :3128, for most HTTP proxies).

For an http:// URL the request goes to the proxy with the absolute URL in the request line and a Proxy-Connection: Keep-Alive header, byte-for-byte what curl sends. An https:// URL is tunnelled through the proxy with CONNECT.

Proxy that needs a login

var result = await Curl.ExecuteAsync(@"
    curl -x http://proxy.example.com:8080 \
         --proxy-user alice:s3cret \
         https://httpbin.org/ip");

Console.WriteLine(result.StatusCode);

The credentials can also go in the proxy URL, -x http://alice:s3cret@proxy.example.com:8080. -U splits at the first colon. Either way they are sent pre-emptively as Proxy-Authorization: Basic ....

When the proxy wants a challenge-based scheme, add --proxy-digest, --proxy-ntlm or --proxy-anyauth (negotiated in-library for plain HTTP requests through the proxy, the same way --digest and --ntlm work for servers); --proxy-basic and --proxy-negotiate are accepted too:

var result = await Curl.ExecuteAsync(
    "curl -x http://proxy.example.com:8080 -U alice:s3cret --proxy-anyauth http://example.com/");

Proxy environment variables and –noproxy

Without -x, CurlDotNet reads the same environment variables curl does: http_proxy for http:// URLs (curl ignores upper-case HTTP_PROXY for safety, and so does CurlDotNet’s own proxy selection), https_proxy or HTTPS_PROXY for https:// URLs, and all_proxy/ALL_PROXY as the fallback. no_proxy/NO_PROXY and the --noproxy option list hosts that are contacted directly: host names (which also match their subdomains), IP addresses, CIDR ranges such as 10.0.0.0/8, or * for everything.

// Use the corporate proxy, except for internal hosts
var result = await Curl.ExecuteAsync(
    "curl -x http://proxy.example.com:8080 --noproxy 'localhost,127.0.0.1,.internal.example.com,10.0.0.0/8' https://httpbin.org/ip");

--noproxy '' overrides NO_PROXY and proxies everything; -x '' tells CurlDotNet not to pick a proxy from the environment variables. When CurlDotNet selects no proxy at all, the request uses .NET’s default HttpClient proxy handling (HttpClient.DefaultProxy), which on Windows follows the system proxy settings.

Tunnels and proxy headers

SOCKS5 proxy

var result = await Curl.ExecuteAsync(
    "curl --socks5 127.0.0.1:1080 https://httpbin.org/ip");

Console.WriteLine(result.Body);

--socks4, --socks4a and --socks5-hostname select the other SOCKS flavours, and -x socks5://127.0.0.1:1080 (or socks4://, socks4a://, socks5h://) does the same through -x. SOCKS uses the SOCKS support built into .NET’s HttpClient handler.

Builder style

using CurlDotNet.Core;

var result = await CurlRequestBuilder.Get("https://httpbin.org/ip")
    .WithProxy("http://proxy.example.com:8080", "alice", "s3cret")
    .ExecuteAsync();

Console.WriteLine(result.Body);

WithProxy(url) without credentials is the -x equivalent.

Reusable client with a proxy

using CurlDotNet.Lib;

using var client = new LibCurl()
    .WithProxy("http://proxy.example.com:8080", "alice", "s3cret")
    .WithTimeout(TimeSpan.FromSeconds(20));

var ip = await client.GetAsync("https://httpbin.org/ip");
Console.WriteLine(ip.Body);

When the proxy is unreachable

Proxy failures carry the same exit codes as curl, and the message names the proxy rather than the target:

Exception Exit code Meaning
CurlCouldntResolveProxyException 5 The proxy’s host name could not be resolved (or the proxy string is not a usable address). Has ProxyHost.
CurlCouldntResolveHostException 6 The target host could not be resolved.
CurlCouldntConnectException 7 The proxy refused the connection, or the tunnel was refused. ViaProxy is true when the proxy was involved.
using CurlDotNet.Exceptions;

try
{
    await Curl.ExecuteAsync("curl -x http://127.0.0.1:9 https://httpbin.org/ip");
}
catch (CurlCouldntResolveProxyException ex)
{
    Console.WriteLine($"Unknown proxy host {ex.ProxyHost}");
}
catch (CurlCouldntConnectException ex) when (ex.ViaProxy)
{
    Console.WriteLine($"Proxy refused the connection: {ex.Message}");
}

A proxy that answers 407 Proxy Authentication Required to a -p tunnel is reported as CurlCouldntConnectException with a hint to pass -U. Add -v to the command to see which proxy was used; the verbose log is prepended to result.Body. See the curl exit code reference for the full list.

Gotchas