All posts
guides8 min readOct 24, 2026, 09:00 AM

curl with a proxy: -x, -U, SOCKS5, socks5h, environment variables and debugging

Every curl option that matters for proxies, tested on curl 8.19: the -x syntax and its default port, credentials with special characters, SOCKS5 and socks5h, headers, environment variables, .curlrc, bypassing the proxy, and what each exit code means.

curl takes a proxy with -x (long form --proxy). For most jobs this one line is all you need:

curl -x http://USERNAME:[email protected]:8080 https://api.ipify.org

If it prints an IP that is not yours, traffic is going through the proxy. The rest of this guide covers every curl option that matters for proxies: the -x syntax and its defaults, credentials with awkward characters, SOCKS5 and socks5h, headers, environment variables, config files, bypassing the proxy, and how to read curl's errors. All examples were checked on curl 8.19.

The -x syntax

The format is:

[scheme://][user:password@]host[:port]

The scheme says how curl talks to the proxy, not to the site:

  • http:// or no scheme: an HTTP proxy. HTTPS sites still work, through a CONNECT tunnel that the proxy cannot read.
  • https://: TLS between you and the proxy itself. Only use it if the proxy port speaks TLS; on a plain HTTP port curl fails with exit code 35.
  • socks5:// and socks5h://: a SOCKS5 proxy, covered below. socks4:// and socks4a:// also exist.

Always write the port. When it is missing, curl assumes 1080, the traditional SOCKS port, for both HTTP and SOCKS proxies (only https:// proxies default to 443). -x http://geo.gw.nyxon.sh would therefore try to speak HTTP to the SOCKS5 port.

-x also overrides any proxy set in environment variables, and -x "" turns an environment proxy off for one command.

Credentials: -U and special characters

Credentials can go in the URL or in a separate option:

curl -x http://geo.gw.nyxon.sh:8080 -U "USERNAME-country-us:PASSWORD" https://api.ipify.org

-U (long form --proxy-user) needs no encoding, which makes it the easy answer for passwords with @, :, / or # in them. In the URL those characters must be percent-encoded: @ is %40, : is %3A, / is %2F, # is %23. curl URL-decodes the user and password it finds in the proxy string. Pasted raw, a password like p@ss:w/rd#1 broke the URL in our test, and curl failed with curl: (5) Could not resolve proxy because it read part of the password as the hostname.

Quote the whole value either way. In bash and zsh, single quotes stop $, ! and & from being interpreted; in PowerShell single quotes work too; in Windows cmd.exe use double quotes.

The curl manual also points out that command-line arguments can be visible to other users of the same machine, if only briefly, in the process list. On shared servers, put credentials in a config file instead (see below).

SOCKS5 and socks5h

Same credentials, the product's SOCKS5 port, and a different scheme:

curl -x socks5h://USERNAME:[email protected]:1080 https://api.ipify.org

The h decides where DNS happens. With socks5://, curl resolves the hostname on your machine and sends the proxy an IP address. With socks5h://, it sends the hostname and the proxy resolves it on the exit side. We watched both on a test proxy: socks5:// arrived as a bare IP, socks5h:// as api.ipify.org. Use socks5h: local lookups reveal to your DNS resolver which sites you visit, and can point you at a CDN node near you instead of near the exit.

The older flags --socks5 and --socks5-hostname do the same as the two schemes; the manual calls --socks5-hostname superfluous now that socks5h:// exists.

An HTTP proxy always receives the hostname, so it has no equivalent of this problem. The SOCKS5 vs HTTP comparison explains the other differences.

Headers: -H versus --proxy-header

For an HTTPS site, curl sends two sets of headers: one to the proxy in the CONNECT request, and one to the site inside the tunnel.

  • -H adds headers for the site. -A sets the user agent the site sees.
  • --proxy-header adds headers for the proxy only, in the CONNECT request.

When you test a real target, set a browser user agent. curl's default, curl/8.19.0, is blocked on sight by many sites, and that has nothing to do with the proxy:

curl -x http://USERNAME:[email protected]:8080 -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36" https://example.com

Environment variables

curl reads these, so scripts pick up a proxy without changes:

  • http_proxy: proxy for http:// URLs. Lowercase only, per the curl manual.
  • HTTPS_PROXY or https_proxy: proxy for https:// URLs.
  • ALL_PROXY or all_proxy: used when no scheme-specific variable is set.
  • NO_PROXY or no_proxy: hosts that go direct.
export https_proxy="http://USERNAME:[email protected]:8080"
export http_proxy="$https_proxy"
curl https://api.ipify.org

The lowercase-only rule for http_proxy trips people up: on Linux and macOS, curl ignores HTTP_PROXY in capitals. On Windows, environment variable names are not case-sensitive, so there HTTP_PROXY and http_proxy are the same variable.

ALL_PROXY accepts SOCKS URLs too, so this sends everything through SOCKS5:

export ALL_PROXY="socks5h://USERNAME:[email protected]:1080"

Bypassing the proxy

NO_PROXY takes a comma-separated list of hosts that skip the proxy, and per the manual it wins even over -x:

NO_PROXY="localhost,127.0.0.1,.internal.example" curl -x http://USERNAME:[email protected]:8080 http://127.0.0.1:3000/health

For a single command, --noproxy does the same. --noproxy '*' disables the proxy for every host, and --noproxy "" overrides a NO_PROXY variable. Entries match the host and its subdomains, and IP ranges can be written in CIDR form, such as 192.168.0.0/16.

Config files

Options can live in a file, one per line, without the leading dashes:

proxy = "http://geo.gw.nyxon.sh:8080"
proxy-user = "USERNAME-country-us:PASSWORD"

Use it for one command with -K file, or save it as .curlrc in your home directory and curl reads it every time. On Windows curl checks %USERPROFILE%\.curlrc and %APPDATA%\.curlrc, among other places, and also accepts the name _curlrc. A proxy in .curlrc applies to every curl call on the machine, including other people's scripts; -q as the very first option skips the file for one run.

Debugging

See the conversation with the proxy. -v prints the CONNECT request and the proxy's answer:

> CONNECT example.com:443 HTTP/1.1
> Proxy-Authorization: Basic ...
< HTTP/1.1 407 Proxy Authentication Required
* CONNECT tunnel failed, response 407

That Proxy-Authorization value is your username and password in base64, which anyone can decode. Remove it before you share verbose output.

Tell proxy errors from site errors. %{http_connect} is the proxy's answer to CONNECT, %{http_code} is the site's:

curl -s -o /dev/null -x http://USERNAME:[email protected]:8080 -w "proxy: %{http_connect}  site: %{http_code}\n" https://example.com

proxy: 200 site: 403 means the proxy worked and the site refused you. proxy: 407 site: 000 means the proxy refused you. The proxy error codes guide covers every combination.

Time each step.

curl -s -o /dev/null -x http://USERNAME:[email protected]:8080 -w "connect %{time_connect}s  tls %{time_appconnect}s  first byte %{time_starttransfer}s  total %{time_total}s\n" https://example.com

time_connect measures the connection to the proxy, and time_appconnect includes the TLS handshake with the site through the tunnel.

Read the exit code. These are the ones proxy users meet, with the messages curl 8.19 printed in our tests:

  • 5: could not resolve the proxy hostname, for example curl: (5) Could not resolve proxy: .... Check for a typo, or a password that needs encoding.
  • 7: failed to connect, for example curl: (7) Failed to connect to ... Could not connect to server. Wrong port, or a firewall blocks it.
  • 28: timeout, for example curl: (28) Operation timed out after ... milliseconds with 0 bytes received.
  • 35: SSL connect error. Often https:// written in front of a plain HTTP proxy.
  • 56: failure receiving data. Covers a refused tunnel, curl: (56) CONNECT tunnel failed, response 407, and the HTTP scheme on a SOCKS port, curl: (56) Proxy CONNECT aborted.
  • 97: proxy handshake error. curl: (97) Received invalid version in initial SOCKS5 response. means SOCKS5 on an HTTP port; curl: (97) User was rejected by the SOCKS5 server (1 1). means wrong SOCKS5 credentials.

Run echo $? in bash, or $LASTEXITCODE in PowerShell, right after curl to see the code.

PowerShell: use curl.exe

In Windows PowerShell 5.1, curl is an alias for Invoke-WebRequest, a different tool. curl -x ... fails there with A parameter cannot be found that matches parameter name 'x'. Type curl.exe to run the real curl, which ships with Windows 10 and 11.

FAQ

How do I use curl with a SOCKS5 proxy? Use the socks5h:// scheme and the SOCKS5 port of your plan: curl -x socks5h://host:port -U USERNAME:PASSWORD URL.

Why does curl ignore my HTTP_PROXY variable? On Linux and macOS curl only reads the lowercase http_proxy for http:// URLs. HTTPS URLs use https_proxy or HTTPS_PROXY.

How do I skip the proxy for one host? Add it to NO_PROXY, or pass --noproxy host.

How do I check where the exit is? Request https://ipinfo.io/json through the proxy and read country, city and org. The 60-second proxy test walks through it.

Try it

Every command here works with any NyxonProxies plan; swap in the host and port shown on your Generate page. For the same examples in Python and Node.js, see the quickstart. New accounts can sign up and link Discord under Freebies for 250MB of free traffic.

Written by NyxonProxies TeamGet 250MB free with Discord