Most curl proxy failures are exit code 7 (could not connect), 56 (the connection died), or 60 (a certificate problem), and our guide to those three covers them. Two others turn up often enough to deserve their own page, and they are different in kind. Exit 5, "Could not resolve proxy", happens before curl has contacted anything: it was told to use a proxy and could not look the proxy's name up. Exit 97, "Proxy handshake error", happens after curl has reached the proxy: the two of them could not agree on how to proceed, which in practice means a SOCKS negotiation failed. One is a configuration leftover, the other a protocol conversation, and neither has much to do with the site you were trying to reach.
Where curl gets a proxy from
curl consults four sources, and the surprise in most exit-5 cases is that the proxy came from one the user forgot.
- The command line.
-xor--proxywith a URL, and the--socks5,--socks5-hostname,--socks4, and--socks4aoptions for SOCKS. ~/.curlrc. A line readingproxy = http://proxy.example.com:3128applies to every command. Windows uses_curlrcin the user's profile directory.- The environment.
http_proxyfor plain HTTP requests,HTTPS_PROXYorhttps_proxyfor HTTPS ones,ALL_PROXYorall_proxyas a fallback for everything, andNO_PROXYorno_proxyfor exclusions. One quirk is worth memorising: curl readshttp_proxyin lower case only, for a security reason dating back to CGI environments, while the HTTPS and ALL variants work in either case. A shell that exportsHTTP_PROXYin upper case therefore affects HTTPS requests only throughHTTPS_PROXY, and plain-HTTP requests not at all. --noproxyon the command line overrides all of the above for the hosts it names, and--noproxy '*'disables proxying for the command entirely.
curl -v settles which is in play. When a proxy is being used, the verbose output says so near the top, naming the address, before any request is sent.
curl (5): Could not resolve proxy
The name after the colon is what curl tried to resolve, and reading it usually explains the error on its own.
| What the name looks like | What went wrong | Fix |
|---|---|---|
proxy.corp.example.com | An office proxy configured on a machine that is now elsewhere | Unset the variable or the .curlrc line off-network |
htp://proxy.example.com:3128 or proxy.example.com;3128 | A typo in the scheme or separator, so the whole string became the hostname | Fix the URL |
proxy.example.com:3128 with a trailing space, or quotes inside the value | Shell quoting put characters into the name | Re-export the variable cleanly |
http://user:pa@ss@proxy.example.com:3128 | An unencoded @ in the password split the URL at the wrong place | URL-encode it: pa%40ss |
| A name you have never seen | A leftover from a tool, a VPN, or unwanted software | Find the source; see below |
Run env | grep -i proxy and cat ~/.curlrc and the source is almost always in one of the two. Then either fix the value, or remove it:
unset http_proxy https_proxy HTTPS_PROXY all_proxy ALL_PROXY
and delete the export from ~/.bashrc, ~/.zshrc, ~/.profile, or the Windows environment variables so it does not return with the next shell. For one command without touching anything, --noproxy '*' bypasses every proxy. For a machine that moves between networks, a shell function that sets the proxy on the office network and unsets it elsewhere beats a permanent export, and NO_PROXY keeps internal hosts direct while the proxy carries the rest.
A proxy name you do not recognise, on a personal machine, deserves the same treatment as an unknown proxy in the system settings: find what set it before removing it. The Windows and macOS side is in proxy settings that keep turning back on, where the same leftovers appear in the system panels.
curl (97): Proxy handshake error
Exit 97 was introduced in curl 7.73.0 so that failures in the conversation with the proxy stop being reported as generic connection errors. It means curl connected to the proxy and the proxy-level negotiation failed. Nearly always that is SOCKS, because SOCKS has a real handshake with distinct failure points, and each of them names its cause.
Run the command with -v and read the lines after the connection is established. The three families:
- Authentication.
SOCKS5 authentication failed, or a rejection at method negotiation. The proxy wants a username and password and got none (add-U user:passor put them in the URL), got the wrong ones, or your address is not on an IP-authenticated proxy's allowlist. - A SOCKS reply. The proxy accepted you and refused the request with a numbered reply: general SOCKS server failure, connection not allowed by ruleset, host unreachable, connection refused, and the rest. Each has a specific meaning and a specific fix, laid out in our guide to SOCKS5 proxy errors.
- The wrong protocol. curl was told the proxy is SOCKS and it is HTTP, or the reverse; the handshake bytes make no sense to the other side, and curl gives up. Port 1080 is conventionally SOCKS, 8080 and 3128 conventionally HTTP, and our proxy checker tests each protocol separately for any address.
For name resolution through SOCKS, prefer --socks5-hostname (the socks5h:// form in URLs) so that the proxy resolves the target; --socks5 resolves locally, which fails for targets that only resolve on the proxy's network and reports as a host-unreachable reply.
Two neighbours worth knowing
curl (35) SSL connect error with a proxy usually means the proxy URL begins with https://. That tells curl to speak TLS to the proxy itself, and nearly every proxy listens in plain HTTP regardless of what it carries. Change the scheme to http://. The https:// form exists for the rare proxy that terminates TLS on its listening port, and for those --proxy-cacert and --proxy-insecure are the proxy-side equivalents of --cacert and -k.
curl in Windows PowerShell 5 is not curl. curl there is an alias for Invoke-WebRequest, which has different options and prints different errors, and -x means nothing to it. Run curl.exe explicitly to get the real curl that ships with Windows 10 and 11, or use PowerShell 7, where the alias was removed.
The full set of curl proxy exit codes
| Exit code | curl's message | Where it fails | Guide |
|---|---|---|---|
| 5 | Could not resolve proxy | Before contacting anything | This page |
| 7 | Failed to connect to proxy | Opening the connection to the proxy | curl 7, 56, 60 |
| 35 | SSL connect error | TLS to a proxy that speaks plain HTTP | This page |
| 56 | Received HTTP code N from proxy after CONNECT, or Recv failure | The proxy's answer to the tunnel, or a dropped connection | curl 7, 56, 60 |
| 60 | SSL certificate problem | Verifying the target, or an inspecting proxy | curl 7, 56, 60 |
| 97 | Proxy handshake error | The SOCKS negotiation with the proxy | This page and the SOCKS5 guide |
Testing a proxy properly from the shell
The command that answers most questions about a proxy in one go:
curl -v -x http://user:pass@proxy.example.com:3128 https://example.com/ -o /dev/null
For SOCKS:
curl -v --socks5-hostname proxy.example.com:1080 -U user:pass https://example.com/ -o /dev/null
The verbose output shows whether the proxy resolved (exit 5 territory), whether it answered (exit 7), what it said to the tunnel (exit 56), and how the SOCKS handshake went (exit 97). Using proxies with curl covers the options in full, and every address on our free proxy list carries the result of the last handshake on each protocol, which is the quickest way to avoid testing a SOCKS proxy that is actually an HTTP one.
Exit 5 and exit 97 side by side
Exit 5: read the name after the colon, find it in env | grep -i proxy or ~/.curlrc, and fix or remove it; --noproxy '*' bypasses it for one command. Exit 97: the proxy answered and the handshake failed, so read -v for the SOCKS detail, check credentials and the allowlist, and confirm the port is actually SOCKS. And remember that http_proxy is lower case only.