cURL with Proxy: How to Set HTTP and SOCKS5 Proxies

Published:

13 minute read

Updated:

PXNET
Written by: PXNET
Very faint IP rows, a plus-cornered frame with the Proxynet wordmark, an x and the cURL logo, tagged INTEGRATION

You have a proxy address from your provider and want to know whether it works before it goes into a scraper, a cron job or a CI pipeline. cURL is the quickest check: one command sends a request through the proxy and prints the IP address the target site sees, and the same command then drops straight into a shell script.

This article covers the -x option, credentials, SOCKS5, proxy settings for every command, a download script with retries and the exact error messages. Every command was run with curl 8.21 against a local test proxy; the options are documented in the curl man page.

What is cURL?

cURL (pronounced "curl") is a command-line tool that transfers data with URLs: it downloads pages and files, sends forms and API requests, and prints or saves the response. The work is done by libcurl, a transfer library that PHP and other languages also use.

Every command has the same form:

bash
curl [options] [URL]

There are more than 250 options. -o page.html saves the response to a file, -I fetches only the headers, -L follows redirects and -x uses a proxy. Options are case sensitive: -x sets a proxy, -X sets the HTTP method.

cURL ships with macOS, with Windows 10 and 11 as curl.exe, and with most Linux distributions; minimal container images may need it installed (apt install curl on Debian and Ubuntu). Run curl --version: if the Features: line contains HTTPS-proxy, your build can also reach a proxy over TLS.

Which protocols does cURL support?

cURL works only with protocols that can be written as a URL. The current release lists DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. curl --version shows what your build supports; RTMP, found in older lists, is gone.

Without a scheme, cURL guesses from the host name (ftp.example.com means FTP, most names HTTP). For proxies this matters in one way: an HTTP proxy carries HTTP and HTTPS; other protocols pass only through a CONNECT tunnel (-p) and only if the proxy allows that port. SOCKS5 carries any TCP connection, so FTP or SMTP through a proxy usually means SOCKS5.

Why use cURL with a proxy?

  • Visible steps. -v prints the connection to the proxy, the CONNECT request and both answers, so you see where a request fails.
  • Every proxy type in one option. HTTP, HTTPS, SOCKS4, SOCKS4a, SOCKS5 and SOCKS5h all go through -x.
  • Scripting. Exit codes, --retry, timeouts and --write-out let a shell script handle failures, and PHP's curl_* functions take the same options.
  • Many URLs in one command. URL globbing such as page[1-20].html or {a,b,c} expands into several requests, all through the same proxy.

Typical jobs: checking an exit IP's country before a data scraping run, testing price monitoring targets from another market, and downloads in scheduled jobs. Node.js equivalents are in cURL in JavaScript, PHP ones in PHP Web Scraping.

How does cURL send a request through a proxy?

  1. cURL connects to the proxy, not to the target.
  2. HTTP target, HTTP proxy: cURL sends the request with the full URL (GET http://example.com/ HTTP/1.1). The proxy can read it.
  3. HTTPS target, HTTP proxy: cURL sends CONNECT example.com:443, with a Proxy-Authorization header if you gave credentials. After the proxy answers 200, TLS to the target runs inside that tunnel, so the proxy sees only the host and port. The CONNECT method is defined in RFC 9110.
  4. SOCKS5 proxy: cURL logs in (with username and password, if given) and asks the proxy to connect to the target (RFC 1928). With socks5:// it sends an IP it resolved itself; with socks5h:// it sends the host name.
  5. The target answers to the proxy's IP. Your IP does not reach it, but your DNS queries can if cURL resolves names locally.

How to set a proxy in cURL: the -x option

curl --help proxy lists every proxy option. The main one:

text
 -x, --proxy <[protocol://]host[:port]>  Use this proxy

-x and --proxy are the same option:

bash
curl -x "http://user:pass@pr.proxynet.io:8000" https://httpbin.org/ip

httpbin.org/ip returns the IP your request came from, so with a working proxy it shows the proxy's exit IP.

  • No scheme means HTTP. -x "user:pass@pr.proxynet.io:8000" behaves like the http:// form.
  • Always write the port. Without it cURL picks a default: in our curl 8.21 test 80 for http://, 443 for https:// and 1080 for SOCKS (the manual still says 1080). None of these is likely to be your provider's port, so the connection fails or times out.
  • Quote the address so the shell does not interpret &, ? or !.
  • -x is per command and overrides proxy environment variables. The next command goes out directly again.

On Proxynet's residential and mobile products, one gateway serves HTTP and SOCKS5 on separate ports, and the panel's Endpoint Generator builds the username with the country, city and session you pick.

How do you use cURL with an HTTP or HTTPS proxy?

An HTTP proxy carries both kinds of target:

bash
# HTTPS target: CONNECT tunnel, the page stays encrypted
curl -x "http://user:pass@pr.proxynet.io:8000" https://httpbin.org/ip

# HTTP target: the proxy forwards the plain request
curl -x "http://user:pass@pr.proxynet.io:8000" http://httpbin.org/ip

An HTTPS proxy (https://) also encrypts the connection to the proxy itself. Use it only if the port speaks TLS; https:// pointed at a plain HTTP port fails in the handshake (SEC_E_INVALID_TOKEN on Windows, typically wrong version number with OpenSSL).

Certificate options are split between the two connections:

  • -k (--insecure) skips certificate checks for the target site only. With an http:// target it does nothing.
  • --proxy-insecure skips the check of an HTTPS proxy's certificate. --proxy-cacert file.pem is safer: it trusts a company proxy's CA without disabling checks.

Keep -k for quick tests, never in scripts that send tokens. For non-HTTP protocols through an HTTP proxy, add -p (--proxytunnel). See the HTTPS Proxy page for how encrypted traffic passes a proxy.

How do you pass a proxy username and password?

Both forms send the same Proxy-Authorization header:

bash
curl -x "http://user:pass@pr.proxynet.io:8000" https://httpbin.org/ip
curl -x "http://pr.proxynet.io:8000" -U "user:pass" https://httpbin.org/ip

Inside the address, special characters in the password must be percent-encoded: @ as %40, : as %3A, # as %23. cURL decodes them before sending; in our test p%40ss reached the proxy as p@ss. With -U (--proxy-user) no encoding is needed. Basic authentication is the default; company proxies may need --proxy-ntlm, --proxy-digest or --proxy-anyauth.

Passwords on the command line land in the shell history. In scripts, read them from a private config file (-K file) or use an IP whitelist instead (Proxynet accepts up to 10 IPs). Proxy Authentication: User:Pass vs IP Whitelist compares both and explains the 407 error.

How do you use cURL with a SOCKS5 proxy?

Write the SOCKS version as the scheme:

bash
curl -x "socks5h://user:pass@pr.proxynet.io:1080" https://httpbin.org/ip
SchemeWho resolves the host nameUsername and passwordUse it for
http://The proxyYesWeb scraping, APIs, most tools
https://The proxyYesA proxy port that speaks TLS
socks5h://The proxyYesSOCKS5 with location-correct DNS
socks5://Your machineYesTargets only your DNS can resolve
socks4a://The proxyUser ID onlyOld SOCKS4 servers
socks4://Your machineUser ID onlyOld SOCKS4 servers

With socks5://, your DNS server sees every host you visit and geo-aware sites answer for your location; socks5h:// avoids both (WebRTC and DNS Leaks explains why that matters). The older --socks5-hostname host:port equals socks5h://, with credentials in -U.

For the protocol differences, see SOCKS vs. HTTP Proxy and What Is a SOCKS5 Proxy?. Proxynet's SOCKS5 Proxy runs on its own port next to the HTTP port.

How do you set a proxy for every cURL command?

MethodScopeExample
-xOne commandcurl -x "http://user:pass@host:port" URL
Environment variableThe shell sessionexport https_proxy="http://user:pass@host:port"
.curlrcEvery run of that userproxy = "http://host:port"

Environment variables. cURL reads http_proxy for HTTP targets, https_proxy or HTTPS_PROXY for HTTPS and ALL_PROXY as a fallback. http_proxy works only in lowercase (a protection for CGI programs), so HTTP_PROXY is ignored on Linux and macOS. In PowerShell: $env:HTTPS_PROXY = "http://user:pass@host:port". wget, pip and git read the same variables; see Using a Proxy with wget and the everything curl page on proxy variables.

The config file. cURL reads ~/.curlrc (Windows: %USERPROFILE%\.curlrc or _curlrc) before every run:

text
proxy = "http://pr.proxynet.io:8000"
proxy-user = "user:pass"

curl -q URL, with -q first, skips it for one run.

Exceptions. --noproxy "localhost,.internal.example" or the NO_PROXY variable sends those hosts directly. A name also matches its subdomains, CIDR ranges like 192.168.0.0/16 work, and the only wildcard is a lone *. NO_PROXY applies even with -x.

cURL has no JavaScript engine, so it cannot run PAC files. If your network uses one, read it (What Is a PAC File?) and pass the proxy it returns with -x.

How do you check that cURL is using the proxy?

Run curl https://api.ipify.org without and with -x: a different address in the expected country means the proxy works. A healthy HTTPS request through an HTTP proxy shows these -v lines:

text
* Establishing HTTP proxy tunnel to httpbin.org:443
> CONNECT httpbin.org:443 HTTP/1.1
> Proxy-Authorization: Basic dXNlcjpwYXNz
< HTTP/1.1 200 Connection Established
* CONNECT tunnel established, response 200

The Proxy-Authorization value is only Base64; remove it before sharing. In scripts, -w prints the result on one line:

bash
curl -s -o /dev/null -x "http://user:pass@pr.proxynet.io:8000" \
  -w "%{http_code} connect=%{http_connect} proxy_used=%{proxy_used}\n" https://httpbin.org/ip

http_connect is the proxy's answer to CONNECT; proxy_used (curl 8.7+) is 1 when a proxy was used. For location, speed and leak checks, see How to Test a Proxy.

Example: downloading a URL list through a proxy with retries

This script downloads the URLs in urls.txt four at a time through the proxy, retries temporary failures and prints each status and duration. It needs cURL and a POSIX shell (Linux, macOS or Git Bash):

bash
#!/usr/bin/env bash
# fetch.sh: download every URL in urls.txt through the proxy, 4 at a time
set -u
PROXY="${PROXY:-http://user:pass@pr.proxynet.io:8000}"

sed 's/^/url = /' urls.txt | curl \
  --parallel --parallel-max 4 \
  --proxy "$PROXY" \
  --connect-timeout 10 --max-time 30 \
  --retry 3 --retry-connrefused \
  --fail --silent --show-error \
  --create-dirs --output-dir pages --remote-name-all \
  --write-out "%{http_code} %{time_total}s %{url}\n" \
  --config -
  • --config - reads the url = … lines from sed and must come last, because --remote-name-all applies only to URLs added after it.
  • --retry 3 retries timeouts and HTTP 408, 429, 500, 502, 503, 504, 522 and 524, waiting 1, 2 and 4 seconds and honouring Retry-After.
  • --max-time stops a stalled transfer; --fail turns HTTP errors into exit code 22.
  • Keep --parallel-max low when all URLs are on one site; too many parallel requests can bring 429 responses.

In our test, four pages were saved and a 503 URL was retried three times before the script exited with code 22.

Repeat a request against api.ipify.org to see whether your gateway rotates: a rotating gateway returns a new IP each time, a sticky session keeps one IP for 1 to 60 minutes on Proxynet. What Is IP Rotation? explains when to use each; Residential Proxy supports both.

Common cURL proxy errors and how to fix them

Messages as printed by curl 8.21 in our tests:

  • curl: (5) Could not resolve proxy: … The proxy host name is wrong: check spelling and stray quotes.
  • curl: (7) Failed to connect to … over proxy … Nothing answers on that port (or cURL fell back to a default port because none was given), or a firewall blocks it.
  • curl: (7) CONNECT tunnel failed, response 407 Wrong credentials, unencoded special characters or an IP not on the whitelist. Older versions print curl: (56) Received HTTP code 407 from proxy after CONNECT; for http:// targets you simply get status 407. See HTTP Status Codes in Web Scraping.
  • curl: (56) Proxy CONNECT aborted The proxy closed the connection; in our test, an http:// address pointed at a SOCKS5 port.
  • curl: (97) Received invalid version in initial SOCKS5 response. A socks5:// address points at an HTTP port. User was rejected by the SOCKS5 server means wrong SOCKS credentials.
  • curl: (28) Connection timed out after … milliseconds No answer in time; see Proxy Server Not Responding.
  • curl: (35) … or SSL_ERROR_SYSCALL The TLS handshake failed: https:// on a plain proxy port, or a device cutting TLS connections.
  • curl: (60) SSL certificate problem: unable to get local issuer certificate Often a company proxy inspecting TLS. Install its CA certificate instead of using -k.
  • The proxy is ignored. Look for NO_PROXY, uppercase HTTP_PROXY or another .curlrc; -v shows which address cURL tries first. Python's version of these failures is covered in Max Retries Exceeded With URL.

Decision guide

NeedRecommendation
Test a new proxy oncecurl -x "http://user:pass@host:port" https://httpbin.org/ip
Password contains @, : or #-U "user:pass", or percent-encode it
DNS resolved at the proxysocks5h://, not socks5://
FTP, SMTP or another non-HTTP protocolSOCKS5, or -p with an HTTP proxy
Proxy for a shell sessionexport https_proxy=… and lowercase http_proxy
Proxy for every runproxy = "…" in ~/.curlrc
No password in scriptsIP whitelist in the panel
Same IP for a login flowSticky session from the Endpoint Generator

Frequently asked questions

Does cURL use the system proxy settings on Windows or macOS?

No. cURL takes a proxy only from -x, from environment variables and from .curlrc. A proxy set in Windows Settings or macOS System Settings does not affect it.

How do I make cURL ignore the proxy for one request?

Add --noproxy "*" or -x "". Either sends that command directly even when https_proxy or .curlrc sets a proxy.

Why does my cURL proxy command fail in PowerShell?

In Windows PowerShell 5.1, curl is an alias for Invoke-WebRequest, which does not know -x. Type curl.exe to run the real cURL. PowerShell 7 no longer has this alias.

What is the difference between socks5 and socks5h in cURL?

With socks5:// your machine resolves the host name; with socks5h:// the proxy does, so DNS matches the proxy's location.

Can I use a proxy with cURL in PHP?

Yes. PHP's cURL functions use libcurl: CURLOPT_PROXY sets the address, CURLOPT_PROXYUSERPWD the credentials and CURLOPT_PROXYTYPE the SOCKS type. Examples are in PHP Web Scraping.

Can cURL rotate proxies on every request?

cURL uses one proxy per command. A rotating gateway assigns a new exit IP per connection, so a loop of commands gets a different IP each time. Several URLs in one command usually reuse one connection and share its IP.

Summary

To use cURL with a proxy, add -x with the full address: scheme, credentials, host and port. Use http:// for most jobs and socks5h:// for SOCKS5 with remote DNS, put the proxy in https_proxy or .curlrc when every command needs it, and keep -k for tests. When a command fails, the exit code and -v output show whether the address, the credentials or the target is at fault. For residential, mobile and SOCKS5 endpoints that work with these commands, see Proxynet proxies.

Ask ChatGPTAsk Claude