The short answer is one flag: curl -x "http://host:port" "https://example.com" routes the request through a proxy, so the target sees the proxy's IP instead of yours. The -x (or --proxy) flag is the whole feature. Everything else in this post is variation: how to add credentials, how to switch protocols, how to set it once for a whole shell, and how to confirm it actually took effect.
If you already know what a proxy server is and just want correct, copy-pasteable commands, skip to the syntax table below. The examples use 127.0.0.1 and obvious placeholders so you can swap in your own host, port, and credentials without guessing which field is which.
The one flag that does it: -x
cURL routes through a proxy when you pass -x (short form) or --proxy (long form). They are identical; pick whichever reads better in your scripts. The value is a URL: scheme, optional credentials, host, and port.
# Route one request through an HTTP proxy curl -x "http://127.0.0.1:8080" "https://example.com" # Same request, long-form flag curl --proxy "http://127.0.0.1:8080" "https://example.com"
The scheme in front of the proxy host (http://, https://, socks5://) tells cURL how to talk to the proxy, not what it fetches. A plain http:// proxy still happily fetches https:// URLs: cURL opens a CONNECT tunnel through it and runs TLS end to end, so the proxy never sees your encrypted bytes. That distinction trips people up, and it is worth reading HTTP vs HTTPS proxies once if it is fuzzy.
Adding proxy authentication
Most paid proxies require credentials. You have two ways to supply them, and they behave slightly differently.
The first puts user and password directly in the proxy URL, before the host, separated by a colon and ended with @:
# Credentials inline in the proxy URL curl -x "http://user:[email protected]:8080" "https://example.com" # Credentials in a separate flag (keeps them out of the URL) curl -x "http://127.0.0.1:8080" -U "user:pass" "https://example.com"
The -U (or --proxy-user) form is worth the habit: it keeps the password out of the URL, which is what tends to leak into shell history and logs. If your username or password contains special characters (@, :, /), URL-encode them, otherwise cURL will misread where the host begins. A literal @ in a password, for instance, becomes %40.
HTTP, HTTPS, and SOCKS in one place
SOCKS proxies work the same way; you only change the scheme. SOCKS sits a layer below HTTP and forwards raw TCP, so it carries any protocol but cannot read or rewrite your requests. Use socks5h:// rather than socks5:// when you want the proxy to resolve DNS (so your machine never leaks the hostname it is looking up); the h stands for "hostname resolution on the proxy." For the deeper tradeoff, see what is a SOCKS5 proxy.
| Goal | Flag and value |
|---|---|
| HTTP proxy | -x "http://host:port" |
| HTTPS proxy | -x "https://host:port" |
| SOCKS5 proxy | -x "socks5://host:port" |
| SOCKS5, DNS on proxy | -x "socks5h://host:port" |
| Proxy auth, inline | -x "http://user:pass@host:port" |
| Proxy auth, separate | -U "user:pass" |
| Bypass proxy for a host | --noproxy "example.com" |
There is also a dedicated --socks5 flag (and --socks4) that takes a bare host:port with no scheme. It is equivalent to -x "socks5://..." and exists mostly for readability when a script juggles several proxy types.
cURL reads both lowercase and uppercase forms of the environment variables, with one historical quirk: it honors lowercase http_proxy but, for safety, only the uppercase HTTPS_PROXY and others. The reason is that HTTP_PROXY collides with a CGI header (Proxy: becomes HTTP_PROXY in some server environments), so cURL deliberately ignores the uppercase HTTP form in CGI contexts. When in doubt, set lowercase for HTTP and you avoid the whole class of surprise.
Setting a proxy for the whole shell with environment variables
Passing -x on every command gets old. Export the proxy once and every cURL call in that shell session uses it, along with most other command-line tools that respect these variables (wget, git, package managers, and so on).
# Apply to every request in this shell session export http_proxy="http://user:[email protected]:8080" export https_proxy="http://user:[email protected]:8080" # Skip the proxy for these hosts, comma-separated export NO_PROXY="localhost,127.0.0.1,.internal.example.com" # Stop using the proxy unset http_proxy https_proxy
Both http_proxy and https_proxy take the same value: the scheme is the protocol cURL uses to reach the proxy, while the variable name selects which target URLs it applies to. NO_PROXY is the escape hatch: any host matching the list connects directly. A leading dot (.internal.example.com) matches all subdomains, and NO_PROXY="*" disables the proxy entirely without unsetting the others.
Making the proxy permanent with .curlrc
Environment variables die with the shell. To make cURL always use a proxy, put it in cURL's config file. On Linux and macOS that is ~/.curlrc; on Windows it is _curlrc in the %APPDATA% directory.
# ~/.curlrc (Linux / macOS) proxy = "http://user:[email protected]:8080" proxy-user = "user:pass"
Every cURL command now routes through the proxy with no flags. That convenience cuts both ways: it is easy to forget the file exists and then spend an hour wondering why one machine behaves differently from another. If a request misbehaves, check ~/.curlrc early. To bypass it for a single call, pass --noproxy "*" or run with -q, which tells cURL to ignore the config file entirely.
Bypassing the proxy for specific requests
When a proxy is set globally (env var or .curlrc) you will sometimes want one request to go direct, an internal health check, say, or a localhost service. --noproxy handles it per command:
# Ignore the proxy entirely for this one request curl --noproxy "*" "http://localhost:3000/health" # Bypass only for one domain, proxy stays on for the rest curl --noproxy "internal.example.com" "http://internal.example.com/api"
Verifying the proxy actually works
Never assume routing took effect: confirm it. The fastest check is to ask an echo service for the IP it sees and compare it to your real one. If the address that comes back is the proxy's, you are routed.
# Your real IP, no proxy curl "https://httpbin.org/ip" # The IP seen through the proxy: should differ curl -x "http://user:[email protected]:8080" "https://httpbin.org/ip" # Add -v to watch the CONNECT handshake and headers curl -v -x "http://user:[email protected]:8080" "https://httpbin.org/ip"
The verbose flag (-v) is the real diagnostic. It prints the connection to the proxy, the CONNECT line for HTTPS targets, the proxy's response, and the request that finally goes out, which tells you exactly where things break when they break.
Common cURL proxy errors and how to read them
Most proxy failures fall into a handful of recognizable shapes. Reading the message rather than retrying blindly saves real time.
| What you see | What it usually means |
|---|---|
Failed to connect to ... port ... |
Wrong host or port, or the proxy is down. Confirm the address and that the port is open. |
Proxy CONNECT aborted / 407
|
Authentication required or rejected. Check your credentials and URL-encode special characters. |
SSL certificate problem |
TLS verification failed for the target, not the proxy. Fix the trust store rather than disabling checks in production. |
Could not resolve proxy |
The proxy hostname itself is wrong or DNS is failing before any request goes out. |
Received HTTP code 403 / 429 |
The proxy connected fine; the target blocked or rate-limited the exit IP. A different IP or a rotating pool is the fix. |
That last row is the one that matters most for scraping. A 403 or 429 means your cURL command is perfect: the target simply does not trust the IP you came from. No flag fixes that; you need a better or rotating IP. If you keep hitting it, how to scrape without getting blocked and proxy status error codes go deeper than a single command can.
On the SSL line: cURL has a -k (--insecure) flag that skips certificate verification. It is fine for poking at a local test box and a genuine hazard against anything you care about, because it disables exactly the check that catches a man-in-the-middle. Treat it as a debugging tool, never a default.
From one proxy to a rotating pool
Everything above works against a single proxy. The moment you scrape at volume, a single IP becomes the bottleneck: the target rate-limits it, then blocks it, and you are back to manually swapping addresses. The usual next step is a list of proxies you rotate through yourself, which means writing the rotation, retry, and health-check logic by hand. See how to use rotating proxies for that approach.
The other option is to put one endpoint in front of the whole pool and let it rotate for you. Your cURL command does not change at all; you just point -x at a gateway instead of a single host, and a fresh exit IP is chosen per request behind it.
Smart AI Proxy is one endpoint you point cURL at: it rotates across a large residential, datacenter, and mobile pool per request and retries on blocks, so the same -x flag you already know gets a fresh, trusted IP every call instead of one that gets banned. Run your real target through it on the free tier first.
# Same -x flag, pointed at a rotating gateway. # Your token is the proxy username; password is empty. curl -x "http://_USER_TOKEN_:@smartproxy.crawlbase.com:8012" -k \ "https://httpbin.org/ip"
That is the only change to your workflow: the gateway swaps the exit IP on the back end while your command stays a plain cURL call. If the target also needs a real browser or server-side retries, a managed fetch like the Crawling API takes a URL and returns the rendered result instead of just handing you a clean IP. Wiring either one up is a read through the API docs.
Key takeaways
-
One flag does the job.
curl -x "scheme://host:port" URLroutes through a proxy;--proxyis the same flag spelled out. -
The scheme picks the protocol to the proxy, not the target. An
http://proxy still fetches HTTPS via aCONNECTtunnel;socks5h://resolves DNS on the proxy. -
Keep credentials out of the URL. Prefer
-U "user:pass"over inline creds, and URL-encode special characters. -
Set it once with env vars or .curlrc.
http_proxy/https_proxycover a session;~/.curlrcmakes it permanent;NO_PROXYand--noproxyare the escape hatches. -
Verify, then read errors. Echo your IP through the proxy and use
-v; a403/429is a blocked IP, not a broken command, and that is where a rotating pool earns its keep.
Frequently Asked Questions (FAQs)
How do I use a proxy with cURL?
Pass the -x (or --proxy) flag with the proxy URL: curl -x "http://host:port" "https://example.com". Add credentials inline as http://user:pass@host:port or, better, with a separate -U "user:pass" flag. The scheme in the proxy URL (http, https, socks5) is how cURL talks to the proxy, not what it fetches.
How do I add a username and password to a cURL proxy?
Either embed them in the proxy URL before the host (-x "http://user:pass@host:port") or supply them separately with -U "user:pass". The separate flag keeps the password out of your shell history. URL-encode any special characters in the credentials, for example a literal @ becomes %40, so cURL parses the host correctly.
How do I set a proxy with environment variables?
Export http_proxy and https_proxy with the proxy URL, and cURL (plus most other command-line tools) will use them for every request in that shell. Use NO_PROXY to list hosts that should connect directly, and unset http_proxy https_proxy to turn it off. Prefer the lowercase http_proxy to avoid a CGI-related conflict with the uppercase form.
Can cURL use a SOCKS5 proxy?
Yes. Use -x "socks5://host:port", or the dedicated --socks5 "host:port" flag. To have the proxy resolve DNS (so your machine never leaks the hostname), use socks5h:// instead. SOCKS forwards raw TCP, so it carries any protocol but cannot read or rewrite your requests the way an HTTP proxy can.
How do I make cURL always use a proxy?
Add a proxy = "http://host:port" line to cURL's config file: ~/.curlrc on Linux and macOS, or _curlrc in %APPDATA% on Windows. Every cURL command then routes through it with no flags. To skip the config for one call, pass -q, or use --noproxy "*" to bypass the proxy for that request only.
Why does my proxy return 403 or 429 errors?
Those codes come from the target site, not from cURL or the proxy, which means the connection worked but the exit IP was blocked or rate-limited. A single proxy IP gets flagged quickly under load. The fix is a different or rotating IP, either by managing a pool yourself or pointing cURL at a rotating gateway that swaps the exit IP per request.
Crawl any site at scale, without fighting infrastructure.
Crawlbase handles proxies, fingerprints, and CAPTCHAs so your team ships data pipelines instead of maintaining crawl plumbing. 1,000 requests free, no card required.
