Using a Proxy with cURL
Pass the proxy to curl as a URL with -x or --proxy, for example curl --proxy http://USER:PASSWORD@HOST:PORT https://api.ipify.org. For SOCKS5 with DNS on the proxy, use socks5h:// instead of http://. To avoid URL-encoding a password, put it in --proxy-user. Add -v to watch the proxy handshake.
In brief
- -x takes a URL. With no scheme curl assumes an HTTP proxy, and with no port it assumes 1080.
- For HTTPS websites curl opens a CONNECT tunnel through an HTTP proxy, so TLS still runs end to end.
- socks5h:// lets the proxy resolve hostnames; socks5:// resolves them on your machine.
- curl reads http_proxy only in lower case, and -x overrides any proxy environment variable.
- -v prints the Proxy-Authorization header. It is Base64, not encryption, so redact it before sharing logs.
HTTP and HTTPS through a proxy
The curl manual defines -x, --proxy as [protocol://]host[:port]. If you give no protocol, or http://, curl treats it as an HTTP proxy. If you give no port, curl uses 1080. Credentials can sit in the URL, where curl URL-decodes them, or in --proxy-user (short form -U).
# HTTP proxy, HTTPS website (curl opens a CONNECT tunnel)
curl -x http://USER:PASSWORD@HOST:PORT https://api.ipify.org
# The same, with the password in its own option (no URL encoding)
curl -x http://HOST:PORT --proxy-user 'USER:PASSWORD' https://api.ipify.org
# Headers only, to see the site's response without the body
curl -sS -I -x http://USER:PASSWORD@HOST:PORT https://example.com
For an HTTPS website, curl asks the HTTP proxy to open a tunnel with the CONNECT method from RFC 9110. The proxy relays encrypted bytes and never sees the page. For a plain http:// website, curl sends the full request to the proxy instead. To force a tunnel for plain HTTP too, add -p (--proxytunnel).
With -I through an HTTP proxy, the output starts with the proxy's own HTTP/1.1 200 Connection established, followed by the website's headers. The first block is the tunnel, not the site.
All commands on this page were run with curl 8.7.1 against a local proxy that requires a username and password. Addresses in the output are documentation addresses.
SOCKS5 and socks5h
SOCKS5 is defined in RFC 1928, with username and password login in RFC 1929. curl speaks it when the proxy URL starts with socks5:// or socks5h://:
# SOCKS5, hostname resolved by the proxy
curl -x socks5h://USER:PASSWORD@HOST:PORT https://api.ipify.org
# Same thing with the dedicated option
curl --socks5-hostname HOST:PORT --proxy-user 'USER:PASSWORD' https://api.ipify.org
# Wrong SOCKS5 password, curl 8.7.1:
curl: (97) User was rejected by the SOCKS5 server (1 1).
The h decides who resolves the website's name. We logged what our test proxy received: with socks5://, curl looked up api.ipify.org itself and sent an IPv4 address; with socks5h://, it sent the hostname. Use socks5h:// so the lookup happens at the exit, matching the exit's location, and your local resolver does not see every hostname.
SOCKS5 is never the default: the manual says -x treats a proxy string with no scheme as an HTTP proxy.
Authentication and encoding
Inside a proxy URL, special characters in the username or password must be percent-encoded: @ is %40, : is %3A. With a password of p@ss:w0rd, the unencoded URL failed with curl: (5) Unsupported proxy syntax ... Port number was not a decimal number between 0 and 65535, because curl read the text after the second @ as the host. The encoded URL and --proxy-user 'lab-user:p@ss:w0rd' both worked. The proxy formats guide has the full encoding table and a converter.
curl sends Basic authentication to proxies by default. If a proxy uses another scheme, --proxy-digest, --proxy-ntlm or --proxy-anyauth, which lets curl pick a method the proxy offers, are available. Most commercial proxies, including Coronium lines, use a username and password over Basic.
One behavior that confuses scripts: with a wrong password and an https:// website, curl exits with curl: (56) CONNECT tunnel failed, response 407. With a wrong password and an http:// website, curl prints the proxy's 407 response and exits with status 0, as if it had succeeded. Add --fail, which turns it into curl: (22) The requested URL returned error: 407, or check %{http_code} with -w when scripting. The 407 guide covers every cause.
Environment variables
Without -x, curl reads proxy settings from the environment. The manual's environment section has one trap worth memorising: variables work in lower or upper case, with lower case taking precedence, except http_proxy, which curl reads only in lower case. We confirmed it: with only HTTP_PROXY set to an unused port, an http:// request went direct and succeeded.
# Lower-case http_proxy is the only form curl reads for http:// URLs
export http_proxy='http://USER:PASSWORD@HOST:PORT'
export https_proxy='http://USER:PASSWORD@HOST:PORT'
export no_proxy='.internal.example,localhost'
curl https://api.ipify.org # uses https_proxy
curl --noproxy api.ipify.org https://api.ipify.org # skips it for this host
curl -x '' https://api.ipify.org # ignores the variables for one call
# ~/.curlrc, read on every run
proxy = "http://HOST:PORT"
proxy-user = "USER:PASSWORD"
ALL_PROXY applies to every protocol, and no_proxy or --noproxy lists hosts to reach directly. -x overrides all of these, and -x '' turns them off for one command. Everything curl, the project's book, explains the order of precedence in more depth.
Reading the -v output
When a proxy connection fails, run the same command with -v. This is the start of a successful CONNECT through an authenticated HTTP proxy:
* Trying 192.0.2.10:8080...
* Connected to 192.0.2.10 (192.0.2.10) port 8080
* CONNECT tunnel: HTTP/1.1 negotiated
* Proxy auth using Basic with user 'lab-user'
* Establish HTTP proxy tunnel to api.ipify.org:443
> CONNECT api.ipify.org:443 HTTP/1.1
> Host: api.ipify.org:443
> Proxy-Authorization: Basic bGFiLXVzZXI6cEBzczp3MHJk
> User-Agent: curl/8.7.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* CONNECT phase completed
* CONNECT tunnel established, response 200
Read it in order. Connected to proves curl reached the proxy, so DNS and the port are right. Proxy auth using Basic with user shows which username curl sent. The > lines are what curl sent to the proxy, and < HTTP/1.1 200 Connection established is the proxy agreeing to open the tunnel. Anything after that is between you and the website.
The Proxy-Authorization line is your username and password in Base64, which anyone can decode. Delete it before pasting logs into a ticket or a chat.
To check just the tunnel status in a script, use -w '%{http_connect}', which prints the proxy's response code to CONNECT.
Errors seen in testing
| Output | Meaning | First thing to check |
|---|---|---|
| curl: (5) Unsupported proxy syntax | curl could not parse the proxy URL | Percent-encode @ and : in credentials |
| curl: (7) Failed to connect | Nothing answered on that host and port | Host, port and whether HTTP or SOCKS port is used |
| curl: (56) CONNECT tunnel failed, response 407 | Proxy refused the credentials for an HTTPS site | Username, password and encoding |
| HTTP 407 printed, exit status 0 | Proxy refused the credentials for an HTTP site | Same as above; add --fail in scripts |
| curl: (97) User was rejected by the SOCKS5 server | SOCKS5 login failed | Credentials, and that the SOCKS port is used |
Each row below was produced on purpose against the local test proxy with curl 8.7.1: a password with unencoded symbols, a closed port, a wrong password for an HTTPS and an HTTP site, and a wrong SOCKS5 password. When a script fails, match the message first; it usually names the layer that failed before you touch any setting.
Sources and review scope
Sources reviewed October 30, 2026. Standards, vendor documents and regulator pages were read on the date above. Where a page shows commands, they were run with curl 8.7.1 and Python Requests 2.32 against a local authenticated test proxy; addresses in the output are replaced with documentation ranges. Vendor parameters change, so confirm them in the vendor documentation linked here.
Frequently asked questions
More proxy questions
Short answers first, then the detail, with the standard or vendor document behind each claim.
Related workflows
Dedicated 4G and 5G carrier lines, with live stock and prices for each country and carrier.
URL and colon formats, encoding and conversion.
Which protocol to use, and when it matters.
REST API v3, SDK, CLI and MCP for Coronium lines.
How SOCKS5 works and where to use it.
Every cause of a 407 and how to fix it per client.
Where to get a dedicated line
A Coronium mobile proxy is one physical device with its own SIM, rented to one customer. It connects over HTTP or SOCKS5, the IP changes by dashboard button, API link or timer, and data is not metered on dedicated lines. Brazil lines are the exception: 20 GB of data a month and a new IP on request only, with no timer. Stock and prices differ by country and carrier.