How to use HTTP and SOCKS proxies with cURL

Sending a cURL request through a specific proxy is useful when outbound traffic must leave through an approved gateway, a debug run must match an application path, or a test must confirm whether the proxy itself is changing the result.

In cURL, --proxy takes the proxy URL and the scheme chooses the proxy type. http:// or no scheme uses an HTTP proxy, https:// negotiates TLS to the proxy itself, socks5h:// lets the SOCKS proxy resolve the destination hostname, and socks5:// resolves the hostname locally before the SOCKS connection starts.

Proxy transport and name resolution are separate choices. Use socks5h:// when the hostname must stay on the proxy side, use socks5:// only when local DNS resolution is intended, and use -q as the first parameter during testing so a local ~/.curlrc file does not quietly change the route. Use --noproxy or NO_PROXY for hosts that must stay direct even when a proxy is defined.

Steps to use HTTP and SOCKS proxies with cURL:

  1. Send a plain HTTP request through the HTTP proxy first so the relay-style request line is explicit.
    $ curl -q -sS -v --proxy http://egress-proxy.example.net:3128 http://api.edge.example.net/health -o /dev/null
    * Connected to egress-proxy.example.net (198.51.100.24) port 3128
    > GET http://api.edge.example.net/health HTTP/1.1
    > Host: api.edge.example.net
    > Proxy-Connection: Keep-Alive
    < HTTP/1.1 200 OK
    ##### snipped #####

    Plain HTTP over an HTTP proxy sends the full target URL in the request line because the proxy fetches the resource on the client's behalf.

  2. Send an HTTPS request through the same HTTP proxy and confirm that cURL opens a CONNECT tunnel before the origin request starts.
    $ curl -q -sS -v --proxy http://egress-proxy.example.net:3128 https://api.edge.example.net/health -o /dev/null
    * Connected to egress-proxy.example.net (198.51.100.24) port 3128
    * Establish HTTP proxy tunnel to api.edge.example.net:443
    > CONNECT api.edge.example.net:443 HTTP/1.1
    > Host: api.edge.example.net:443
    < HTTP/1.1 200 Connection established
    * CONNECT tunnel established, response 200
    ##### snipped #####
    > GET /health HTTP/2
    < HTTP/2 200

    HTTPS targets already use CONNECT through an HTTP proxy. Related: How to force proxy CONNECT tunneling with cURL

  3. Use socks5h:// when the SOCKS proxy should resolve the destination hostname.
    $ curl -q -sS -v --proxy socks5h://socks-gateway.example.net:1080 https://api.edge.example.net/health -o /dev/null
    * Connected to socks-gateway.example.net (198.51.100.41) port 1080
    * SOCKS5 connect to api.edge.example.net:443 (remotely resolved)
    * SOCKS5 request granted.
    ##### snipped #####
    < HTTP/2 200

    Use this form when local DNS must stay out of the path or when the target hostname only resolves from the proxy side.

  4. Use socks5:// only when local DNS resolution is intended before the SOCKS connection starts.
    $ curl -q -sS -v --proxy socks5://socks-gateway.example.net:1080 https://api.edge.example.net/health -o /dev/null
    * Host api.edge.example.net:443 was resolved.
    * IPv4: 192.0.2.25
    * SOCKS5 connect to 192.0.2.25:443 (locally resolved)
    * SOCKS5 request granted.
    ##### snipped #####
    < HTTP/2 200

    This exposes the destination lookup to the local resolver. For legacy proxies, socks4a:// keeps proxy-side DNS and socks4:// keeps local DNS, but prefer SOCKS5 when the proxy supports it.

  5. Bypass the proxy for one host with --noproxy when the request must go direct without changing the base proxy setting.
    $ curl -q -sS -v --proxy http://egress-proxy.example.net:3128 --noproxy api.edge.example.net https://api.edge.example.net/health -o /dev/null
    * Host api.edge.example.net:443 was resolved.
    * Connected to api.edge.example.net (192.0.2.25) port 443
    ##### snipped #####
    < HTTP/2 200

    NO_PROXY uses the same host matching and disables proxy use even when --proxy is set for a matching target. Related: How to use proxy environment variables with cURL