How to Use cURL with Proxies: Syntax, Examples, and Tips

Sophie Marchand Sophie Marchand 15 min read

Master cURL with proxy connections using the -x flag, SOCKS support, authentication, environment variables, and debugging techniques for proxy issues.

Basic cURL Proxy Syntax: The -x Flag

cURL's proxy support starts with a single flag: -x (or its long form --proxy). The syntax is straightforward — append -x followed by your proxy URL to any cURL command. The proxy URL format follows the standard convention: protocol://host:port. When you run a command like curl -x http://proxy.example.com:8080 https://target-site.com, cURL routes the request through the specified proxy instead of connecting directly to the target.

The protocol in the proxy URL tells cURL how to communicate with the proxy server. Use http:// for standard HTTP proxies (the most common type), https:// for proxies that accept TLS-encrypted connections, socks4:// for SOCKS version 4 proxies, socks4a:// for SOCKS4 with remote DNS resolution, socks5:// for SOCKS version 5 proxies, and socks5h:// for SOCKS5 with remote DNS resolution. The distinction between socks5 and socks5h matters: socks5 resolves the target hostname on your local machine before sending it to the proxy, while socks5h sends the hostname to the proxy for resolution — preventing DNS leaks.

If you omit the protocol prefix and just specify host:port, cURL defaults to HTTP. This implicit behavior is convenient but can cause confusion when you intend to use a SOCKS proxy and forget the prefix. Always include the protocol explicitly to avoid ambiguity.

For HTTPS target URLs, cURL automatically uses the CONNECT method to establish a tunnel through an HTTP proxy. You do not need to configure this — cURL detects the HTTPS scheme in the target URL and initiates the tunnel. The proxy sees only the target host and port during the CONNECT handshake; it cannot inspect the encrypted traffic that flows through the tunnel afterward.

Proxy Authentication: Credentials in cURL

When your proxy requires username and password authentication, cURL provides two ways to supply credentials. The -U flag (or --proxy-user) accepts credentials in the username:password format. This is the cleanest approach because it separates proxy credentials from the proxy URL, making commands easier to read and less error-prone with special characters.

Alternatively, embed credentials directly in the proxy URL: -x http://username:password@host:port. This format is compact but introduces parsing complications when passwords contain characters like @, :, or /. If your password includes these characters, you must URL-encode them — replace @ with %40, : with %3A, / with %2F, and so on. The -U flag avoids this encoding requirement because cURL handles the special characters internally when constructing the Proxy-Authorization header.

cURL sends proxy credentials using Basic authentication by default, which Base64-encodes the username:password string in the Proxy-Authorization header. While Base64 is not encryption, this is acceptable when your connection to the proxy uses TLS (https:// proxy URL) or when the proxy is on a trusted network. For proxies that require Digest, NTLM, or Negotiate authentication, use the --proxy-digest, --proxy-ntlm, or --proxy-negotiate flags respectively to change the authentication scheme.

A security note: credentials passed via -U or in the proxy URL are visible in your shell's process list and command history. On shared systems, this exposes your proxy credentials to other users. For sensitive environments, use environment variables (covered in the next section) or read credentials from a file using -U with the @filename syntax in combination with a .netrc file.

Environment Variables for System-Wide Proxy Configuration

cURL respects several environment variables that configure proxy behavior without requiring the -x flag on every command. Setting these variables applies proxy routing to all cURL requests in the current shell session — and often to other command-line tools as well, since many follow the same convention.

The primary variables are http_proxy and https_proxy (lowercase). Set http_proxy to your proxy URL for HTTP targets and https_proxy for HTTPS targets. The format is identical to the -x flag: protocol://user:pass@host:port. cURL checks these variables automatically when no explicit -x flag is provided. The uppercase variants HTTP_PROXY and HTTPS_PROXY also work, but lowercase takes precedence if both are set. Some environments use ALL_PROXY as a catch-all that applies to all protocols when protocol-specific variables are not set.

The NO_PROXY variable (also no_proxy in lowercase) is equally important. It specifies a comma-separated list of hostnames, domains, or IP addresses that should bypass the proxy and connect directly. Set NO_PROXY to include localhost, 127.0.0.1, and any internal domains your applications need to reach without proxying. Without NO_PROXY, setting http_proxy or https_proxy routes every cURL request through the proxy, including requests to your own infrastructure — which can cause surprising failures in local development environments.

To verify which environment variables cURL is using, run curl with the -v flag and observe the output. The verbose output shows whether cURL detected proxy environment variables and which proxy it is using for the connection. This is invaluable for debugging environments where multiple tools or shell profiles might set conflicting proxy variables.

HTTPS Through Proxies: CONNECT Tunneling Explained

When cURL sends an HTTPS request through an HTTP proxy, it does not send the full URL to the proxy as it would for an HTTP request. Instead, cURL uses the HTTP CONNECT method to ask the proxy to establish a raw TCP tunnel to the target's port 443. The proxy opens the connection, and from that point forward, cURL negotiates TLS directly with the target server through the tunnel. The proxy relays encrypted bytes in both directions without being able to read them.

This CONNECT tunneling is automatic in cURL when the target URL uses HTTPS. You can force CONNECT tunneling even for HTTP URLs by adding the -p flag (or --proxytunnel). This is useful when you want the proxy to act purely as a network relay without inspecting or modifying the request — some proxies inject headers or modify content on non-tunneled HTTP requests, and -p prevents that.

Certain proxy configurations and firewalls block the CONNECT method or restrict it to specific ports (typically 443 and 563). If your HTTPS requests fail through a proxy while HTTP requests work, the proxy likely blocks or does not support CONNECT. The verbose output (curl -v) will show the CONNECT handshake attempt and the proxy's response — look for a 403 or 405 status in response to the CONNECT request.

TLS certificate verification still happens end-to-end between cURL and the target server, even through a proxy tunnel. The proxy cannot present a fake certificate for the target domain (unless you have explicitly imported a corporate CA certificate that enables TLS interception). If you encounter TLS errors that only occur through the proxy, the proxy might be performing TLS interception — common in corporate environments. Do not use -k to disable verification unless you understand the security implications.

Debugging Proxy Connections with Verbose Output

The -v flag is your primary diagnostic tool when proxy connections behave unexpectedly. cURL's verbose output traces every step of the connection process, from DNS resolution to the final response, and shows proxy-specific handshake details that are invisible in normal output.

When using a proxy with -v, the output reveals several critical stages. First, cURL shows the connection attempt to the proxy server itself — you will see lines indicating it is connecting to your proxy host and port rather than the target. If the connection fails here, the proxy is unreachable (check host, port, and firewall rules). Next, for HTTPS targets, cURL shows the CONNECT request sent to the proxy, including the target hostname and port. The proxy's response to CONNECT appears immediately after — a 200 means the tunnel was established successfully, while 403 or 407 indicates access or authentication issues.

After the CONNECT tunnel is established, the verbose output shows the TLS handshake with the target server — the same information you would see in a direct connection. This helps distinguish between proxy issues (which appear before the TLS handshake) and target server issues (which appear during or after it).

For even more detail, the --trace flag writes a complete hex dump of all data sent and received, including the exact bytes of the proxy authentication exchange. This is useful when debugging authentication failures where -v does not show enough detail — you can inspect the Proxy-Authorization header value to verify encoding is correct. The --trace-ascii flag provides the same information in ASCII-only format, which is more readable for text-based protocols.

Combine -v with -o /dev/null to see only the connection trace without the response body cluttering the output.

Using cURL Proxies with Cookies and Sessions

Maintaining sessions through a proxy with cURL requires the same cookie management you would use for direct connections, with the added constraint that your proxy IP must remain consistent for the session to stay valid.

The -c flag (or --cookie-jar) saves response cookies to a file, and the -b flag (or --cookie) sends cookies from a file with the request. Use both together in a multi-request workflow: the first request saves cookies with -c, and subsequent requests load them with -b. The cookie jar file is a plain text Netscape format file that stores domain, path, expiration, and the cookie name-value pairs.

For sessions that require authentication, chain your cURL commands to first perform a login request (with -c to capture the session cookie), then use the session cookie for subsequent data requests (with -b to send it). When doing this through a proxy, ensure every request in the session chain uses the same proxy — switching proxy IPs mid-session invalidates most session cookies because server-side session validation often includes IP binding.

If your proxy provider supports sticky sessions, use the session parameter in your proxy credentials to maintain the same exit IP across multiple cURL requests. This pairs naturally with cookie-based session management: the sticky proxy ensures IP consistency, and the cookie jar ensures state continuity. Without sticky sessions on a rotating proxy, each cURL request may exit through a different IP, breaking any server-side session that performs IP validation.

The -L flag (follow redirects) is important for proxied sessions. Many login flows redirect multiple times (POST to /login, redirect to /dashboard), and each redirect must carry the cookies and route through the same proxy. Combine -L with -b and -c to handle redirect chains correctly.

Automating cURL Proxy Workflows in Scripts

cURL's command-line nature makes it a natural fit for shell scripts that automate proxy-based data collection. A few patterns make these scripts robust enough for unattended operation.

Store proxy configuration in a .curlrc file (or _curlrc on Windows) in your home directory. This file accepts the same flags as the command line, one per line. Adding proxy = http://host:port and proxy-user = username:password to .curlrc applies proxy settings to all cURL commands without repeating them in every script. For project-specific configurations, cURL supports a -K flag to load a config file from any path.

For scripts that need to cycle through multiple proxies, store proxy URLs in a text file (one per line) and read them in a loop. Read a proxy from the file, make the request, check the exit code and HTTP status code, and log the result. Use cURL's -w flag (write-out) with format variables to capture the HTTP status code, total time, and other metrics without parsing the response body. The format string %{http_code} returns the status code, %{time_total} returns the request duration, and %{remote_ip} shows the IP cURL connected to (which will be the proxy IP for proxied requests).

Handle failures with cURL's --retry flag, which automatically retries on transient errors (connection refused, timeout, HTTP 408/429/500/502/503/504). Combine --retry with --retry-delay to set the interval between retries and --retry-max-time to cap the total retry duration. For proxy-specific retry logic — like switching to a different proxy on failure — you need script-level logic since cURL's built-in retry reuses the same proxy.

Always set --max-time (total request timeout) and --connect-timeout (connection phase timeout) in automated scripts. Without these, a hung proxy connection will block your script indefinitely.

Common cURL Proxy Errors and How to Resolve Them

Proxy-related cURL errors have specific causes that are diagnosable once you know what to look for. Here are the most frequent failures and their solutions.

curl: (7) Failed to connect to proxy host:port — cURL could not establish a TCP connection to the proxy server. Verify the proxy host and port are correct. Test basic connectivity with a TCP check to the proxy port. If the proxy is behind a firewall, ensure your IP is allowed. If using a hostname, verify DNS resolves it correctly.

curl: (56) Received HTTP code 407 from proxy after CONNECT — The proxy requires authentication and your credentials are missing or wrong. Double-check username and password. If using URL-embedded credentials, verify special characters are properly URL-encoded. Try the -U flag instead of URL-embedded credentials to rule out encoding issues.

curl: (35) SSL connect error through proxy — The TLS handshake with the target server failed after the proxy tunnel was established. This is usually a target-side issue, not a proxy issue. The proxy might be interfering with TLS (corporate TLS inspection), or the target may require specific TLS versions or ciphers. Try --tlsv1.2 or --tlsv1.3 to force a specific TLS version.

curl: (28) Operation timed out — Either the proxy server or the target server did not respond within the timeout. Increase --connect-timeout for slow proxy connections or --max-time for slow target responses. If timeouts occur consistently, the proxy server may be overloaded or the target may be blocking the proxy IP with a timeout rather than an explicit rejection.

Empty or truncated responses — If cURL completes without error but returns incomplete data, the proxy may be modifying responses (some HTTP proxies strip content or inject headers). Use -p to force CONNECT tunneling, which prevents proxy modification, or switch to an HTTPS proxy URL.

cURL vs wget: Proxy Capabilities Compared

Both cURL and wget are command-line HTTP tools that support proxies, but their capabilities diverge in ways that matter for proxy-based workflows.

cURL's proxy support is more comprehensive. It handles HTTP, HTTPS, SOCKS4, SOCKS4a, SOCKS5, and SOCKS5h proxies. It supports multiple proxy authentication methods (Basic, Digest, NTLM, Negotiate). It provides fine-grained control over CONNECT tunneling with the -p flag. And its verbose output gives detailed proxy handshake diagnostics that wget cannot match.

wget's advantage is recursive downloading with built-in proxy support. When you need to mirror an entire website or download all files matching a pattern, wget's -r (recursive) flag handles link following, depth limiting, and file filtering while respecting proxy settings. cURL has no recursive capability — you would need to script the link extraction and download loop yourself.

wget reads proxy settings from the same environment variables as cURL (http_proxy, https_proxy, no_proxy) and also from its own configuration file (~/.wgetrc). However, wget's SOCKS support is more limited — it requires compilation with SOCKS support and does not offer the protocol variants (socks4a, socks5h) that cURL provides. For SOCKS proxy use cases, cURL is the clear choice.

For proxy testing and debugging, cURL is superior due to its verbose output, write-out format variables, and trace capabilities. For bulk downloading through a proxy, wget's recursive mode and built-in rate limiting (--limit-rate, --wait) make it more practical. Many practitioners use cURL for testing and development and switch to wget or custom scripts for production downloads.

When to Use cURL for Proxy Testing vs Production Workloads

cURL is an exceptional proxy testing and debugging tool, but it has structural limitations that make it suboptimal for production-scale proxy workloads. Understanding the boundary between testing and production use prevents you from building fragile systems.

cURL excels at proxy validation tasks: testing connectivity to a new proxy, verifying authentication works, checking proxy geographic location by hitting an IP echo service, measuring latency through the proxy with -w %{time_total}, and diagnosing issues with verbose output. Keep a set of cURL commands in your operational runbook for these common checks. A single curl -x proxy:port -v -o /dev/null -w "%{http_code} %{time_total}" https://target-site.com tells you the HTTP status code and total response time through the proxy — invaluable for quick health checks.

For production workloads — anything involving thousands of requests, complex rotation logic, error handling, or data processing — move to a programming language with proper HTTP libraries. cURL in a bash loop lacks connection pooling (each request opens and closes a TCP connection), has no built-in concurrency (you need GNU parallel or background processes for parallelism), and makes error handling and retry logic awkward to implement in shell script.

The transition point is typically around 100-500 requests per job. Below that, a cURL script is fast to write and adequate. Above that, the overhead of no connection pooling, no persistent sessions, and shell-based error handling starts to cost more in execution time and reliability than writing proper code. Python with requests or Node.js with Axios will outperform a cURL script at scale while being easier to maintain and extend.

Frequently Asked Questions

How do I use a SOCKS5 proxy with cURL?
Use the -x flag with the socks5:// protocol prefix: curl -x socks5://host:port https://target-site.com. For SOCKS5 with remote DNS resolution (which prevents DNS leaks), use socks5h:// instead. Add authentication with the -U flag: curl -x socks5h://host:port -U username:password https://target-site.com. The socks5h variant is recommended for privacy because it resolves hostnames on the proxy server rather than your local machine.
Why does cURL ignore my http_proxy environment variable?
Several things can override environment variables. The -x flag on the command line takes precedence over environment variables. A .curlrc configuration file with a proxy setting also overrides environment variables. Additionally, the NO_PROXY or no_proxy variable might match your target URL, causing cURL to bypass the proxy. Use curl -v to see which proxy cURL is actually using, and check --noproxy '' to temporarily clear the no-proxy list for testing.
How do I stop cURL from using a proxy for specific domains?
Set the NO_PROXY environment variable with a comma-separated list of domains: export NO_PROXY=localhost,127.0.0.1,.internal.company.com. The dot prefix (.internal.company.com) matches all subdomains. On the command line, use the --noproxy flag: curl --noproxy 'localhost,.internal.com' -x http://proxy:8080 https://target.com. This ensures internal services and localhost connections bypass the proxy while external traffic routes through it.
Can I use cURL to test if my proxy is leaking my real IP?
Yes. Make a request through your proxy to an IP echo service: curl -x http://your-proxy:port https://api.ipify.org. The returned IP should be the proxy's IP, not yours. For a more thorough check, compare results with and without the proxy: run the same command without -x to see your real IP, then with -x to see the proxy IP. If both return the same IP, your proxy configuration is not being applied. Check with -v to verify cURL is actually connecting through the proxy.
How do I handle proxy passwords with special characters in cURL?
Use the -U flag instead of embedding credentials in the proxy URL. The command curl -x http://host:port -U 'user:p@ss:w0rd' handles special characters in the password correctly because cURL encodes them internally. If you must use URL-embedded credentials, URL-encode special characters manually: @ becomes %40, : becomes %3A, # becomes %23, / becomes %2F. For example: curl -x http://user:p%40ss%3Aw0rd@host:port https://target.com.

Start Collecting Data Today

35M+ IPs across 200+ countries. Pay as you go, starting at $0.50/GB.

Latest from the Blog

Expert guides on proxies, web scraping, and data collection.

Start Using Rotating Proxies Today

Join 8,000+ users using Databay's rotating proxy infrastructure for web scraping, data collection, and automation. Access 35M+ residential, datacenter, and mobile IPs across 200+ countries with pay-as-you-go pricing from $0.50/GB. No monthly commitment, no connection limits - start collecting data in minutes.