๐ŸŽ‰ Premium Proxies ยท 24-Hour Free TrialClaim Now
Guides

curl Proxy CONNECT Aborted? Exit Codes 5, 7, 35, 56 Fixed

curl proxy CONNECT aborted, (7) failed to connect, or a 407 after CONNECT? Map each curl exit code to the hop that failed, read the -v line, and fix it.

S SparkProxy 2 3 min read
Share

A curl proxy CONNECT aborted error, curl: (56) Proxy CONNECT aborted, means the proxy hung up on you before it answered your CONNECT request. It sits in a small family of curl failures you only meet through proxies: (5) Could not resolve proxy, (7) Failed to connect, (28) Connection timed out, (35) TLS errors, (97) SOCKS errors, and the various forms of a 407 after CONNECT. Each exit code points at a specific hop between your terminal and the target. This guide gives you the lookup table, a test harness you can reuse for any proxy, and the fix for each cause.

Every error string below was captured on 2026-10-30 against SparkProxy's live gateway from three curl builds: curl 8.21.0 (Schannel, the build in our Windows Server's System32), curl 8.15.0 (Git for Windows), and curl 7.81.0 with OpenSSL 3.0.2 (Ubuntu 22.04's packaged build). Our test machines are not on the account's IP whitelist, which is how we produced the 407s on demand.

Exit code lookup table: which hop failed

An HTTPS request through an HTTP proxy crosses four hops in order: DNS for the proxy host, TCP to the proxy, the CONNECT exchange with the proxy, then TLS and HTTP with the target inside the tunnel. A curl exit code tells you which hop broke, and the matching -v line confirms it. The names come from curl's own libcurl error list.

Exitlibcurl nameMessage you see (verbatim, curl 8.21.0 unless noted)Hop that failed`-v` line to look for
5CURLE_COULDNT_RESOLVE_PROXY`Could not resolve proxy: gateway.sparkproxy.invalid`DNS for the proxy host`* Could not resolve proxy:` with no `Trying` line
6CURLE_COULDNT_RESOLVE_HOST`Could not resolve host: nope.sparkproxy.invalid`DNS for the target, done locally (`socks5://`)No proxy lines; your resolver failed
7CURLE_COULDNT_CONNECT`Failed to connect to www.sparkproxy.io:443 over proxy gateway.sparkproxy.io after 2852 ms: Could not connect to server`TCP to the proxy`* connect to 65.109.70.130 port 11009 ... failed: Connection refused`
7 (8.20.0+) / 56 (older)CURLE_COULDNT_CONNECT / CURLE_RECV_ERROR`CONNECT tunnel failed, response 407`Proxy auth or ACL`< HTTP/1.1 407 Proxy Authentication Required`
56 (7.86.0 and older)CURLE_RECV_ERROR`Received HTTP code 407 from proxy after CONNECT`Proxy auth or ACLSame `< HTTP/1.1 407` line
28CURLE_OPERATION_TIMEDOUT`Connection timed out after 4001 milliseconds`TCP to the proxy, or a protocol mismatch`* Trying ...` and then nothing until the timeout
35CURLE_SSL_CONNECT_ERROR`schannel: next InitializeSecurityContext failed: SEC_E_INVALID_TOKEN`; OpenSSL: `error:0A00010B:SSL routines::wrong version number`TLS to the proxy (or to the target, if the tunnel opened)TLS lines appear before any `> CONNECT`
56CURLE_RECV_ERROR`Proxy CONNECT aborted`The proxy closed the socket mid-CONNECT`> CONNECT ...` sent, then `* closing connection` with no `<` reply
97CURLE_PROXY`cannot complete SOCKS5 connection to www.sparkproxy.io. (2)`SOCKS5 handshakeThe number in brackets is the SOCKS reply code

Two rules fall out of this table. A < line from the proxy means the proxy is up and talking, so anything after it is a configuration or permission problem. No < line at all means you never got a word out of the proxy, so look at the network, the port, or the protocol.

Status codes the proxy returns after a tunnel opens (502, 503, 429 and friends) are a separate topic, covered in proxy error codes explained.

Diagnosis order, cheapest check first

Work through the hops in the order a packet crosses them. Each check takes seconds and rules out a whole column of the table.

  1. Read the exit code and the bracketed message. curl -sS keeps the progress meter off but still prints the error. That alone usually tells you the hop.
  2. Resolve the proxy name. nslookup gateway.sparkproxy.io should return an address. If it doesn't, you have exit 5 and nothing else matters yet.
  3. Open the port without curl. nc -vz -w 5 gateway.sparkproxy.io 11000 on Linux or macOS, Test-NetConnection gateway.sparkproxy.io -Port 11000 in PowerShell. Success here and exit 7 from curl means curl is aimed somewhere different from what you think: check for an exported https_proxy.
  4. Run curl -v and find the first < line. It separates "proxy unreachable" from "proxy refused me".
  5. Swap only the proxy scheme or port. If http:// works where https:// failed, or 13000 works with socks5h:// where http:// died, you had a protocol mismatch, not an outage.

Credentials come last because they can only fail once the proxy is reachable. The -U versus -u mix-up, env var precedence and the socks5 versus socks5h DNS split are explained in web scraping with curl and proxies; this post assumes your flags are spelled right and focuses on what the failures mean.

Free trial

Scraping at scale? Skip the blocks.

Fast, unblockable datacentre proxies with unlimited bandwidth.

A reusable proxy test harness

curl can report every field you need for a diagnosis through --write-out, so a test harness is one command. %{exitcode} and %{errormsg} were added in curl 7.75.0, and %{http_connect} holds the status code the proxy returned to CONNECT, per the curl manpage.

proxytest() {
  curl -s -o /dev/null --connect-timeout 8 --max-time 20 -x "$1" \
    -w 'exit=%{exitcode} connect=%{http_connect} http=%{http_code} dns=%{time_namelookup}s tcp=%{time_connect}s tls=%{time_appconnect}s\n  %{errormsg}\n' \
    "${2:-https://www.sparkproxy.io/robots.txt}"
}

proxytest "http://USERNAME:PASSWORD@gateway.sparkproxy.io:11000"   # Random Rotation
proxytest "http://USERNAME:PASSWORD@gateway.sparkproxy.io:11002"   # Sticky Sessions
proxytest "socks5h://USERNAME:PASSWORD@gateway.sparkproxy.io:13000" # SOCKS5

Two runs from our non-whitelisted box show how much the fields say:

exit=7 connect=407 http=000 dns=0.004790s tcp=0.176600s tls=0.000000s
  CONNECT tunnel failed, response 407
exit=56 connect=000 http=000 dns=0.004431s tcp=0.173811s tls=0.000000s
  Proxy CONNECT aborted

In both, tcp is non-zero, so the TCP connection to the proxy worked. The first run got an answer (407). The second got none: the CONNECT went out and the socket closed.

If you want a verdict instead of raw fields, this Python wrapper calls the same curl with %{json} (curl 7.70.0 or later) and names the failing hop. It checks http_connect before the exit code on purpose, for reasons the 407 section explains.

import json, subprocess, sys

PROXY = sys.argv[1] if len(sys.argv) > 1 else "http://gateway.sparkproxy.io:11000"
TARGET = "https://www.sparkproxy.io/robots.txt"

out = subprocess.run(
    ["curl", "-s", "-o", "NUL" if sys.platform == "win32" else "/dev/null",
     "--connect-timeout", "8", "-x", PROXY, "-w", "%{json}", TARGET],
    capture_output=True, text=True,
).stdout
r = json.loads(out)
code, tunnel, msg = r["exitcode"], r.get("http_connect", 0), r.get("errormsg") or ""

if code == 5:
    hop = "DNS for the proxy host"
elif code == 6:
    hop = "DNS for the target (resolved on your side)"
elif tunnel and tunnel != 200:
    hop = f"proxy answered CONNECT with {tunnel}: auth, whitelist or a proxy-side refusal"
elif code == 7:
    hop = "TCP to the proxy (refused or unreachable)"
elif code == 28:
    hop = "no answer in time: firewall drop, wrong IP, or protocol mismatch"
elif code == 35:
    hop = "TLS: https:// scheme on a plain proxy, or the target handshake inside the tunnel"
elif code == 56:
    hop = "proxy closed the connection mid-CONNECT: wrong port type or proxy-side drop"
elif code == 97:
    hop = "SOCKS handshake refused by the proxy"
elif code == 0:
    hop = f"tunnel OK, target returned HTTP {r['http_code']}"
else:
    hop = "see curl's exit code list"

print(f"exit={code} connect={tunnel} {r.get('curl_version', '').split()[0]}")
print(f"  {msg}")
print(f"  failing hop: {hop}")

Output from three of our runs:

exit=7 connect=407 libcurl/8.21.0
  CONNECT tunnel failed, response 407
  failing hop: proxy answered CONNECT with 407: auth, whitelist or a proxy-side refusal
exit=35 connect=0 libcurl/8.21.0
  schannel: next InitializeSecurityContext failed: SEC_E_INVALID_TOKEN (0x80090308) - The token supplied to the function is invalid
  failing hop: TLS: https:// scheme on a plain proxy, or the target handshake inside the tunnel
exit=97 connect=0 libcurl/8.21.0
  cannot complete SOCKS5 connection to www.sparkproxy.io. (2)
  failing hop: SOCKS handshake refused by the proxy

Drop the harness into a container entrypoint or a CI step and you'll know a proxy problem before your scraper does. For broader checks such as exit IP, geolocation and leak tests, see how to test if your proxy is working.

Exit 5 and 6: whose DNS lookup failed

Exit 5 is your machine failing to resolve the proxy's hostname. curl never opens a socket, so -v shows the lookup failure and no Trying line:

* Could not resolve proxy: gateway.sparkproxy.invalid
curl: (5) Could not resolve proxy: gateway.sparkproxy.invalid

Check spelling first, then your resolver. A hostname that worked last month and fails now is usually a stale copy-paste from old docs, or a VPN that took over DNS. SparkProxy's gateway is gateway.sparkproxy.io; anything else in a SparkProxy snippet is out of date.

Exit 6 through a proxy is rarer, and it tells you something useful. With an HTTP proxy or socks5h://, the proxy resolves the target name, so you'll never see exit 6 for a bad target; you'll see a CONNECT error instead. Exit 6 shows up with socks5:// (lowercase, no h), because curl resolves the target locally before handing an IP to the proxy:

curl: (6) Could not resolve host: nope.sparkproxy.invalid

If a target resolves through socks5h:// but not through socks5://, the name exists but your local resolver can't see it. Use socks5h://.

Exit 7: refused, unreachable, or a rejected tunnel

Exit 7 has two very different meanings in current curl, and the message tells them apart.

A TCP refusal. Something answered at that IP with a reset because nothing listens on the port. The -v output names the refusal outright:

*   Trying 65.109.70.130:11009...
* connect to 65.109.70.130 port 11009 from 0.0.0.0 port 59552 failed: Connection refused
* Failed to connect to www.sparkproxy.io:443 over proxy gateway.sparkproxy.io after 2852 ms: Could not connect to server

Watch the wording, which moved between versions. curl 8.21.0 names the target and adds "over proxy". curl 8.15.0 named the proxy port: Failed to connect to gateway.sparkproxy.io port 11009 after 2808 ms: Could not connect to server. curl 7.81.0 on Linux said ... port 11009 after 2 ms: Connection refused. If you grep logs for the proxy's port number, the 8.21.0 form slips past. Also note the timing: Linux reported the refusal in 2 ms, while both Windows builds took about 2.8 seconds, so on Windows a slow exit 7 is not proof of a firewall.

The fix is the endpoint. SparkProxy listens on 11000 (HTTP/HTTPS, Random Rotation), 11002 (Sticky Sessions, one IP held for five minutes) and 13000 (SOCKS5). A port from another provider's example, or a missing port, gets refused. Why HTTP and SOCKS services sit on separate ports is covered in proxy ports explained.

A rejected tunnel. Since curl 8.20.0, a proxy that answers CONNECT with any non-2xx status also exits 7, with CONNECT tunnel failed, response 407 (or 403, 502 and so on). Here TCP worked fine. That case gets its own section below.

Exit 28: the hop that never answers

A timeout means curl sent packets and heard nothing back before the deadline:

*   Trying 10.255.255.1:11000...
* Connection timed out after 4002 milliseconds
curl: (28) Connection timed out after 4002 milliseconds

The Trying line with nothing after it is the signature. Compare it with a refusal: a refusal proves a machine is there, a timeout suggests nothing is reachable. Typical causes, in order of how often we see them: an egress firewall that allows only 80 and 443 out, a local firewall or endpoint agent, or a hard-coded IP that no longer routes.

The less obvious cause is a protocol mismatch that hangs. We pointed socks5h:// at the HTTP port 11000. TCP connected, curl sent its SOCKS greeting, and the HTTP proxy sat waiting for a request line it would never get:

curl: (28) Connection timed out after 10002 milliseconds

So a timeout against a port you know is open is worth one scheme check before you call the network team. --connect-timeout covers DNS, TCP and any TLS handshake, per the curl manpage, so raising it won't rescue a mismatch; it only slows the failure down. Choosing separate connect and read budgets for production code is covered in proxy timeouts and retry logic. curl's source also has a Proxy CONNECT aborted due to timeout message, which exits 28 when the overall timer runs out mid-CONNECT.

Exit 35: TLS spoken to a plain proxy

Exit 35 is a failed TLS handshake. Through a proxy there are two candidates, and the order of the -v lines tells you which.

If TLS lines appear before any > CONNECT, curl tried TLS with the proxy itself. That happens when the proxy URL starts with https:// but the proxy speaks plain HTTP. curl 8.21.0 on Schannel:

* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* schannel: next InitializeSecurityContext failed: SEC_E_INVALID_TOKEN (0x80090308) - The token supplied to the function is invalid
curl: (35) schannel: next InitializeSecurityContext failed: SEC_E_INVALID_TOKEN (0x80090308) - The token supplied to the function is invalid

The same mistake on Linux with OpenSSL 3.0.2 reads curl: (35) error:0A00010B:SSL routines::wrong version number. Different text, one cause: curl's TLS ClientHello hit a server that replied in plain HTTP. Change the proxy scheme to http://:

curl -sS -x http://USERNAME:PASSWORD@gateway.sparkproxy.io:11000 https://www.sparkproxy.io/robots.txt

Your traffic to an https:// target is still encrypted end to end. curl asks the proxy to open a tunnel in clear text, then runs TLS with the target through it, as described in the CONNECT method explained.

If the < HTTP/1.1 200 reply to CONNECT appears before the TLS failure, the proxy did its job and the handshake with the target failed. Corporate TLS inspection, an outdated CA bundle, or a target with unusual TLS settings are the suspects. A different proxy rarely helps there; curl -v straight to the target, without the proxy, usually reproduces it.

Exit 56: Proxy CONNECT aborted

This is the error most people search for, and the verbose output is short:

*   Trying 65.109.70.130:13000...
* Establishing HTTP proxy tunnel to www.sparkproxy.io:443
> CONNECT www.sparkproxy.io:443 HTTP/1.1
> Host: www.sparkproxy.io:443
> User-Agent: curl/8.21.0
> Proxy-Connection: Keep-Alive
>
* closing connection #0
curl: (56) Proxy CONNECT aborted

TCP connected, curl sent CONNECT, and the far end closed the socket without sending a single byte of HTTP back. In curl's source (lib/cf-h1-proxy.c), "Proxy CONNECT aborted" is raised when the read returns end-of-file while curl is still waiting for the CONNECT response headers. It is not a 4xx or 5xx; there is no status to read.

We produced it by sending an HTTP CONNECT to SparkProxy's SOCKS5 port, 13000. A SOCKS server receives CONNECT www... where it expects a binary greeting, can't parse it, and closes. All three curl builds we tested returned exit 56 with the identical message, so unlike the 407, this one hasn't moved.

Causes, cheapest to check first:

  1. Wrong port type. An http:// proxy URL aimed at a SOCKS port. The fix is the scheme that matches the port: socks5h://gateway.sparkproxy.io:13000, or http:// on 11000.
  2. A middlebox that kills unknown traffic. Some corporate firewalls and IDS appliances let TCP through to odd ports but reset the connection once they see the payload. If nc connects but every CONNECT aborts, test from another network.
  3. The proxy dropping you deliberately. Some proxies close the socket instead of returning a 407 or 403 when a client IP is banned or a connection limit is exceeded. If the abort is intermittent and correlates with load, compare your concurrency against your plan's limit.
  4. A proxy process that crashed or restarted between accepting your TCP connection and reading the request. Retrying once is reasonable here, unlike for the causes above.

The bash harness shows the difference instantly: connect=000 with a non-zero tcp= time is an abort, while connect=407 is a refusal you can fix with credentials.

The 407 that changed exit codes between curl versions

Here is something we didn't expect. The same request, no credentials, against the same gateway, produced three outputs:

curl buildMessageExit
7.81.0 (Ubuntu 22.04, OpenSSL 3.0.2)`Received HTTP code 407 from proxy after CONNECT`56
8.15.0 (Git for Windows, Schannel)`CONNECT tunnel failed, response 407`56
8.21.0 (Windows, Schannel)`CONNECT tunnel failed, response 407`7

We checked the source tags to pin both changes. curl 7.86.0 still prints "Received HTTP code"; 7.87.0 (released 2022-12-21, per curl's changelog) switched to "CONNECT tunnel failed, response %d". The exit code moved in commit a186ecf, first shipped in curl 8.20.0 (2026-04-29). The commit message says it changes the error "from 56 (RECV_ERROR) for everything to 7 (COULDNT_CONNECT) when the server response could be read successfully, but establishing the connection is not possible".

What this breaks: any shell script that does if [ $? -eq 56 ]; then echo "proxy auth failed" now treats a 407 as a network failure on new curl, and may retry it in a loop. Classify on %{http_connect} instead, which carried 407 on every build we tried:

code=$(curl -s -o /dev/null -w '%{http_connect}' -x "$PROXY" https://www.sparkproxy.io/robots.txt)
[ "$code" = "407" ] && { echo "proxy rejected credentials or IP"; exit 1; }

The fix for the 407 itself depends on your auth mode. With IP whitelisting, add the failing machine's public egress IP in the dashboard; CI runners and cloud hosts often leave from an IP you never registered. With username and password, pass them through -U 'USERNAME:PASSWORD' or the proxy URL, percent-encoding any @, : or /. The Proxy-Authenticate: Basic realm="SparkProxy" header in the -v output confirms the gateway expects Basic auth. The challenge-response flow is explained in how proxy authentication works.

One trap: for a plain http:// target the 407 doesn't fail at all. Our run returned exit 0 with %{http_code} = 407, because the proxy's reply is just an HTTP response. Add -f and curl exits 22 with The requested URL returned error: 407.

Exit 97: SOCKS5 handshake refusals

Exit 97 (CURLE_PROXY, "Proxy handshake error") covers SOCKS failures. With no credentials, and again with wrong ones, port 13000 returned:

curl: (97) cannot complete SOCKS5 connection to www.sparkproxy.io. (2)

The (2) is the SOCKS server's reply code. RFC 1928 defines 0x02 as "connection not allowed by ruleset", which for a client that isn't whitelisted and has no valid login amounts to the SOCKS version of a 407. Other numbers mean other things: 0x05 is "connection refused" by the target, 0x04 "host unreachable". In libcurl code, CURLINFO_PROXY_ERROR (added in 7.73.0) returns the same detail as a named constant such as CURLPX_REPLY_NOT_ALLOWED.

Fix it like a 407: whitelist the IP or pass credentials in socks5h://USERNAME:PASSWORD@gateway.sparkproxy.io:13000. SOCKS setup in Python, Node.js and browsers is in our SOCKS5 setup guide.

Fixes that need a new proxy vs fixes that need you

Switching proxies is the reflex. Most of these errors survive the switch because the fault is on your side of the connection.

Exit and causeDoes another proxy fix it?The actual fix
5: proxy hostname won't resolveNoCorrect hostname, working resolver
6: target won't resolve via `socks5://`NoUse `socks5h://`
7: refused portOnly if the new one has the right portCorrect host and port
7 or 56: CONNECT 407NoWhitelist your egress IP or fix credentials
28: egress firewall dropNo, any blocked port stays blockedOpen the port or use an HTTPS-only route
28 or 56: scheme and port mismatchNoMatch `http://` or `socks5h://` to the port
35: `https://` on a plain proxyNo`http://` proxy scheme
35: target TLS inside a working tunnelRarelyFix CA bundle or TLS inspection
56: proxy dropping connectionsYes, if the proxy is overloaded or banning youLower concurrency, or fail over
97: SOCKS reply 2NoWhitelist or credentials

When an egress firewall blocks every proxy port and nobody will open one, you can still go through SparkProxy over port 443 with the Scraping API, which fetches from its own network:

curl -sS -G "https://scrape.sparkproxy.io/api/v1" \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "url=https://www.sparkproxy.io/robots.txt" \
  -d render_js=false -d proxy_type=premium

render_js defaults to true and costs 5 credits, so render_js=false keeps a plain fetch at 1 credit; new accounts get 1,000 free credits. Add json_response=true if you need the job_id, status_code, duration_ms and credits_used envelope, and base64-decode its body before searching it. format only converts content and does not produce that envelope.

For the proxy route, SparkProxy plans start from $75/month (USD, read from sparkproxy.io on 2026-10-09) with unlimited bandwidth, so harness runs and retries don't add traffic charges. The 24-hour free trial needs no credit card, which is enough time to run the harness from every machine that will use the proxy and whitelist each egress IP before production.

Frequently asked questions

FAQ

curl: (56) Proxy CONNECT aborted means curl sent a CONNECT request and the proxy closed the connection without replying. The usual cause is an http:// proxy URL pointed at a SOCKS port; middleboxes and proxies that drop banned clients produce it too.

curl 8.20.0 (2026-04-29) changed CONNECT failures with a readable status from exit 56 to exit 7, in commit a186ecf. Scripts that test for 56 should check %{http_connect} instead, which reports the proxy's status on every version.

Exit 5 means your machine couldn't resolve the proxy's own hostname. Exit 6 means it couldn't resolve the target, which through a proxy only happens with socks5://, where curl does the target lookup locally.

If TLS errors appear before any CONNECT line in curl -v, you used https:// for a plain HTTP proxy; switch the scheme to http://. If the proxy answered 200 to CONNECT first, the TLS failure is with the target, not the proxy.

It's the SOCKS5 reply code from the server. RFC 1928 defines 2 as "connection not allowed by ruleset", which usually means your IP isn't whitelisted or your credentials are missing or wrong.

Run curl with -w 'exit=%{exitcode} connect=%{http_connect} tcp=%{time_connect}s %{errormsg}'. A non-zero tcp time with connect=000 is an abort, connect=407 is an auth problem, and exit 0 with connect=200 means the tunnel works.

Special Discount ยท 20% off

Get 20% off your first month

Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.

Save up to 15% more on quarterly, half-yearly and yearly plans

Claim Discount

About the Author

The SparkProxy Technical Team builds and runs SparkProxy's datacenter and residential proxy network, the gateway.sparkproxy.io endpoints and the Scraping API. Every curl output in this guide comes from our own runs on 2026-10-30 across curl 7.81.0, 8.15.0 and 8.21.0, with the version named beside each string so you can match it to your terminal.

Keep reading

Related articles

SSL WRONG_VERSION_NUMBER With a Proxy: The Fix

Getting SSL WRONG_VERSION_NUMBER through a proxy in Python? It's almost always https:// in the proxy URL. The one-character fix, plus every SSLEOFError cause.

SparkProxyยทGuides