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
curl -x http://gate.example.com:7777 https://api.ipify.org-x (or --proxy) sets the proxy. Add credentials inline:
curl -x http://USER:PASS@gate.example.com:7777 https://api.ipify.orgOr separately (handy when the password has special characters):
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
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.orgPrefer socks5h to avoid DNS leaks. See SOCKS5 proxies.
HTTPS proxies (TLS to the proxy)
curl -x https://USER:PASS@secure-proxy.example.com:443 https://api.ipify.orgThe 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
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.orgcurl 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:
proxy = "http://gate.example.com:7777"
proxy-user = "USER:PASS"Debugging with verbose output
curl -v -x http://USER:PASS@gate.example.com:7777 https://httpbin.org/ipWhat to look for:
Connected to gate.example.comconfirms the proxy is reachable.CONNECT httpbin.org:443is the tunnel request.HTTP/1.1 200 Connection establishedmeans the proxy accepted it.HTTP/1.1 407means authentication failed.- The TLS handshake and final response follow.
See how proxies work and common proxy errors.
Useful checks
Which IP and location?
curl -s -x http://USER:PASS@gate.example.com:7777 https://ipinfo.io/jsonWhich headers reach the target? (plain HTTP shows proxy-added headers)
curl -s -x http://USER:PASS@gate.example.com:7777 http://httpbin.org/headersTiming breakdown:
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.comSee proxy speed test and how to check if a proxy works.
Testing a proxy list
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.txtProxy 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
| Option | Purpose |
|---|---|
-x, --proxy | Proxy URL (scheme, host, port, optional credentials) |
-U, --proxy-user | Proxy credentials as user:pass |
--proxy-basic, --proxy-digest, --proxy-ntlm | Proxy authentication method |
--proxy-insecure | Skip TLS verification for an HTTPS proxy |
--proxy-cacert | CA bundle for an HTTPS proxy |
--noproxy | Hosts to bypass ('*' for all) |
--socks5-hostname | Legacy way to use SOCKS5 with remote DNS |
-p, --proxytunnel | Force CONNECT tunnelling even for plain HTTP |
--preproxy | A SOCKS proxy to reach the HTTP proxy through (simple chaining) |
Testing many targets through one proxy
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"
doneChecking rotation
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 -cMostly 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:
curl --preproxy socks5h://socks.example.com:1080 -x http://USER:PASS@gate.example.com:7777 https://api.ipify.orgFor longer chains, use proxychains. See proxy chains.
Saving responses for debugging
curl -s -D headers.txt -o body.html -x http://USER:PASS@gate.example.com:7777 https://www.target.example/pageInspect 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:
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:
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:
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:
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/accountWhen 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
-vin curl and log headers in your code). - TLS fingerprint (curl's differs from Python's or Node's).
- Proxy scheme (
socks5vssocks5h). - 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:
curl -s -o /dev/null -x http://USER:PASS@gate.example.com:7777 \
-w '%{json}\n' https://example.com >> results.jsonlEach 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:
jq -r '[.http_code, .time_total] | @tsv' results.jsonl | sort | uniq -c | sort -rn | headSome 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
-xsets the proxy; credentials can go in the URL or in--proxy-user.- Use
socks5h://so DNS resolves through a SOCKS proxy. -vshows the CONNECT exchange and is the fastest way to tell proxy errors from target errors.- curl reads lowercase
http_proxyfor HTTP targets;HTTPS_PROXY,ALL_PROXYandNO_PROXYwork in either case. -wtiming 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.
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.