Web Scraping & Automation

Using curl With Proxies: Every Option You Need

How to use curl with proxies: -x, --proxy-user, SOCKS5 and socks5h, HTTPS proxies, environment variables, .curlrc, bypass lists, verbose debugging and testing proxy speed.

By Team of Monkeys Editorial Updated 4 min read Affiliate disclosure

This guide contains affiliate links to Geonode. If you buy through them we may earn a commission, at no extra cost to you.

Why curl is the best proxy testing tool

When a proxy misbehaves in your scraper or browser, the fastest way to isolate the problem is curl. It's installed on nearly every system, supports every proxy type, and its verbose mode shows exactly what happens between you, the proxy and the target. If it works in curl, the proxy is fine and the problem is in your code.

The basics

bash
curl -x http://gate.example.com:7777 https://api.ipify.org

-x (or --proxy) sets the proxy. Add credentials inline:

bash
curl -x http://USER:PASS@gate.example.com:7777 https://api.ipify.org

Or separately (handy when the password has special characters):

bash
curl -x http://gate.example.com:7777 --proxy-user 'USER:P@ss:word' https://api.ipify.org

-U is short for --proxy-user.

SOCKS proxies

bash
curl -x socks5://USER:PASS@socks.example.com:1080 https://api.ipify.org    # local DNS
curl -x socks5h://USER:PASS@socks.example.com:1080 https://api.ipify.org   # DNS via proxy
curl -x socks4a://socks.example.com:1080 https://api.ipify.org

Prefer socks5h to avoid DNS leaks. See SOCKS5 proxies.

HTTPS proxies (TLS to the proxy)

bash
curl -x https://USER:PASS@secure-proxy.example.com:443 https://api.ipify.org

The https:// scheme on the proxy encrypts the connection to the proxy itself. Add --proxy-insecure only if the proxy uses a self-signed certificate you trust. See HTTP vs HTTPS proxies.

Environment variables

bash
export https_proxy="http://USER:PASS@gate.example.com:7777"
export http_proxy="$https_proxy"
export no_proxy="localhost,127.0.0.1,.internal.example.com"
curl https://api.ipify.org

curl reads lowercase http_proxy (uppercase HTTP_PROXY is ignored for security reasons), and either case for HTTPS_PROXY, ALL_PROXY and NO_PROXY. Use --noproxy '*' to bypass all proxies for one command.

.curlrc

Set a default proxy in ~/.curlrc:

text
proxy = "http://gate.example.com:7777"
proxy-user = "USER:PASS"

Debugging with verbose output

bash
curl -v -x http://USER:PASS@gate.example.com:7777 https://httpbin.org/ip

What to look for:

  • Connected to gate.example.com confirms the proxy is reachable.
  • CONNECT httpbin.org:443 is the tunnel request.
  • HTTP/1.1 200 Connection established means the proxy accepted it.
  • HTTP/1.1 407 means authentication failed.
  • The TLS handshake and final response follow.

See how proxies work and common proxy errors.

Useful checks

Which IP and location?

bash
curl -s -x http://USER:PASS@gate.example.com:7777 https://ipinfo.io/json

Which headers reach the target? (plain HTTP shows proxy-added headers)

bash
curl -s -x http://USER:PASS@gate.example.com:7777 http://httpbin.org/headers

Timing breakdown:

bash
curl -s -o /dev/null -x http://USER:PASS@gate.example.com:7777 \
  -w 'connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n' \
  https://example.com

See proxy speed test and how to check if a proxy works.

Testing a proxy list

bash
while IFS=: read -r host port user pass; do
  ip=$(curl -s --max-time 15 -x "http://$user:$pass@$host:$port" https://api.ipify.org)
  echo "$host:$port -> ${ip:-FAILED}"
done < proxies.txt

Proxy tip: Want to skip proxy management on some targets? Geonode offers both raw proxies and a Scraper API that returns HTML or Markdown with optional JavaScript rendering. We compare the two approaches in testing Geonode with cURL.

curl proxy options reference

OptionPurpose
-x, --proxyProxy URL (scheme, host, port, optional credentials)
-U, --proxy-userProxy credentials as user:pass
--proxy-basic, --proxy-digest, --proxy-ntlmProxy authentication method
--proxy-insecureSkip TLS verification for an HTTPS proxy
--proxy-cacertCA bundle for an HTTPS proxy
--noproxyHosts to bypass ('*' for all)
--socks5-hostnameLegacy way to use SOCKS5 with remote DNS
-p, --proxytunnelForce CONNECT tunnelling even for plain HTTP
--preproxyA SOCKS proxy to reach the HTTP proxy through (simple chaining)

Testing many targets through one proxy

bash
for url in https://www.example.com https://httpbin.org/ip https://api.ipify.org; do
  code=$(curl -s -o /dev/null -w '%{http_code} %{time_total}' -x http://USER:PASS@gate.example.com:7777 "$url")
  echo "$url -> $code"
done

Checking rotation

bash
for i in $(seq 1 10); do
  curl -s -x http://USER:PASS@gate.example.com:7777 https://api.ipify.org; echo
done | sort | uniq -c

Mostly unique IPs means rotation is working. Repeats suggest sticky sessions, connection reuse or a thin pool. See what is proxy rotation.

Simple chaining with --preproxy

curl can reach an HTTP proxy via a SOCKS proxy:

bash
curl --preproxy socks5h://socks.example.com:1080 -x http://USER:PASS@gate.example.com:7777 https://api.ipify.org

For longer chains, use proxychains. See proxy chains.

Saving responses for debugging

bash
curl -s -D headers.txt -o body.html -x http://USER:PASS@gate.example.com:7777 https://www.target.example/page

Inspect headers.txt for status and cookies, and body.html for block pages. When a scraper fails, reproducing the request in curl with the same proxy and headers quickly shows whether the proxy or your code is at fault. See common proxy errors.

curl recipes for everyday proxy tasks

Check exit IP and organisation in one line:

bash
curl -s -x http://USER:PASS@gate.example.com:7777 https://ipinfo.io/json | grep -E '"(ip|country|city|org)"'

Send realistic browser headers through a proxy:

bash
curl -s -x http://USER:PASS@gate.example.com:7777 \
  -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0 Safari/537.36' \
  -H 'Accept-Language: en-US,en;q=0.9' \
  --compressed https://www.example.com/

Follow redirects and show the chain:

bash
curl -sIL -x http://USER:PASS@gate.example.com:7777 https://example.com | grep -iE '^(HTTP|location)'

Keep cookies across requests through the same sticky session:

bash
P="http://USER-session-abc123:PASS@gate.example.com:7777"
curl -s -c jar.txt -b jar.txt -x "$P" https://www.example.com/ -o /dev/null
curl -s -c jar.txt -b jar.txt -x "$P" https://www.example.com/account

When curl works but your code doesn't

That's useful information: the proxy and target are fine, so the difference is in your client. Compare:

  • Headers (use -v in curl and log headers in your code).
  • TLS fingerprint (curl's differs from Python's or Node's).
  • Proxy scheme (socks5 vs socks5h).
  • Environment variables your code might read.
  • Connection reuse affecting rotation.

See Python requests proxies, Node.js proxies and common proxy errors.

Logging every timing at once with %{json}

When you're comparing proxies or keeping records, -w '%{json}' prints all of curl's write-out variables as one JSON object: status, IPs, timings, sizes and more. That makes results easy to save and analyse:

bash
curl -s -o /dev/null -x http://USER:PASS@gate.example.com:7777 \
  -w '%{json}\n' https://example.com >> results.jsonl

Each line in results.jsonl is one request. Fields worth looking at include http_code, remote_ip, time_connect, time_appconnect, time_starttransfer, time_total and size_download. With jq, a quick summary is one line:

bash
jq -r '[.http_code, .time_total] | @tsv' results.jsonl | sort | uniq -c | sort -rn | head

Some practical uses:

  • Provider comparisons: run the same URL list through each provider and keep one JSONL file per provider.
  • Regression checks: keep a baseline and compare after changing servers, regions or plans.
  • Support tickets: a few lines of JSON with timestamps are far more useful to a provider than "it's slow".

Note that remote_ip is the address curl connected to, which is the proxy, not the exit IP; use an IP echo service for the exit. The everything curl guide to proxies covers the proxy options in depth. For fair speed comparisons, see how to test proxy speed.

Key takeaways

  • -x sets the proxy; credentials can go in the URL or in --proxy-user.
  • Use socks5h:// so DNS resolves through a SOCKS proxy.
  • -v shows the CONNECT exchange and is the fastest way to tell proxy errors from target errors.
  • curl reads lowercase http_proxy for HTTP targets; HTTPS_PROXY, ALL_PROXY and NO_PROXY work in either case.
  • -w timing variables give connect, TLS and time-to-first-byte measurements for quick speed tests.
  • If a request works in curl but not in your code, the problem is in your client configuration. See common proxy errors.

FAQ

Why does curl ignore my HTTP_PROXY variable?

curl only reads the lowercase http_proxy for plain HTTP. Use lowercase or pass -x.

How do I bypass the proxy for one request?

Use --noproxy '*' or set no_proxy.

Does curl support proxy chains?

Not directly. Use a tool like proxychains. See proxy chains.

Affiliate disclosure: Team of Monkeys is a Geonode affiliate. Links and banners marked as sponsored, and links to Geonode in the text, use our referral link, and we may earn a commission if you buy through them, at no extra cost to you. Our explanations and comparisons are written to be useful whichever provider you choose.

Editorial note: Proxy providers change their pools, features and prices often, so treat any figures here as typical ranges at the time of writing and confirm current terms with the provider before you buy.