<!-- https://pkizerotohero.com/errors/curl-35-ssl-connect-error/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Fix curl: (35): read the reason after the code, then fix the port or proxy URL, the TLS versions and ciphers, or the client certificate the server wants.

Source: https://pkizerotohero.com/errors/curl-35-ssl-connect-error/ · Last reviewed: 2026-10-20

- curl
- Exact text checked with the real tool

# curl: (35) … wrong version number

Also seen in: git, PHP, other programs built on libcurl

**In one sentence:**

curl's TLS handshake failed before any certificate check; the text after (35) says why: wrong version number means the other side did not answer with TLS at all.

[Check yours: Paste your error](https://pkizerotohero.com/tools/error-finder/)

## Where you see it

curl exits with code 35, `CURLE_SSL_CONNECT_ERROR`: “A problem occurred somewhere in the SSL/TLS handshake” ([libcurl error codes](https://curl.se/libcurl/c/libcurl-errors.html)). libcurl’s short name for it is “SSL connect error”. The curl command prints the TLS library’s own reason instead, and that reason is what matters:

```text
$ curl https://api.example.com:8080/
curl: (35) OpenSSL/3.0.13: error:0A00010B:SSL routines::wrong version number
```

Older versions print the same reason in other forms, such as `curl: (35) error:0A00010B:SSL routines::wrong version number` (curl 7.81) or `curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number` (OpenSSL 1.1.1). PHP apps show it as `cURL error 35`.

| Text after `curl: (35)` | What happened |
| --- | --- |
| `wrong version number` (`packet length too long` with most OpenSSL 3.2 to 3.6 releases) | The other side did not answer with TLS |
| `tlsv1 alert protocol version` | No TLS version in common |
| `sslv3 alert handshake failure` (`ssl/tls alert handshake failure` with OpenSSL 3.2 and later) | No cipher suite in common, or the server wants a client certificate |
| `tlsv1 alert unknown ca` | The server rejected your client certificate |
| `SSL_ERROR_SYSCALL` or `Recv failure: Connection reset by peer` | The connection was closed or reset during the handshake |
| Windows `schannel: … SEC_E_INVALID_TOKEN`, `SEC_E_UNSUPPORTED_FUNCTION`, `SEC_E_ILLEGAL_MESSAGE` | Schannel’s names for the same problems |

## What it means

Before curl sends a request over HTTPS, it runs a [TLS handshake](https://pkizerotohero.com/handbook/glossary/#tls-handshake) with the server: they agree on a TLS version and a [cipher suite](https://pkizerotohero.com/handbook/glossary/#cipher-suite), and the server sends its certificate. Code 35 means this failed before curl got to check the certificate. Certificate problems have their own code, 60, such as [unable to get local issuer certificate](https://pkizerotohero.com/errors/curl-unable-to-get-local-issuer-certificate/).

## Likely causes

### Something other than TLS answers: wrong version number

curl sent a TLS ClientHello and got back something else, usually a plain-HTTP answer. Typical reasons: an `https://` URL for a port that speaks plain HTTP, or a proxy URL that starts with `https://` for a proxy that speaks plain HTTP.

**Confirm:** a plain request to the same port works, and your proxy settings show the scheme:

```bash
curl -v http://api.example.com:8080/
env | grep -i proxy
```

```text
$ curl -x https://proxy.example.com:3128 https://example.com/
curl: (35) OpenSSL/3.0.13: error:0A00010B:SSL routines::wrong version number
```

**Fix:** use the right scheme or port, for example `http://` for the plain service, or the port where the server really speaks TLS. For an ordinary proxy, the proxy URL starts with `http://` even for HTTPS traffic: `export https_proxy=http://proxy.example.com:3128`.

### No TLS version or cipher suite in common

The server speaks only old TLS versions, or only cipher suites your curl does not offer. **Confirm** with OpenSSL:

```bash
openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2 </dev/null
openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_3 </dev/null
```

**Fix it on the server:** enable TLS 1.2 and 1.3 with modern cipher suites. See [How to configure TLS securely](https://pkizerotohero.com/guides/tls-configuration/) and [ERR\_SSL\_VERSION\_OR\_CIPHER\_MISMATCH](https://pkizerotohero.com/errors/chrome-err-ssl-version-or-cipher-mismatch/).

### The server wants a client certificate

With [mutual TLS](https://pkizerotohero.com/handbook/glossary/#mutual-tls) over TLS 1.2, a missing client certificate gives `alert handshake failure`, and one from the wrong CA gives `alert unknown ca`, both with code 35. Over TLS 1.3 the rejection arrives just after the handshake, as `curl: (56) OpenSSL SSL_read: … tlsv13 alert certificate required` or `… tlsv1 alert unknown ca`.

**Fix:** send the certificate and key the server expects, with the intermediate in the certificate file if there is one:

```bash
curl --cert client-chain.pem --key client.key https://api.example.com/
```

See [tlsv1 alert unknown ca](https://pkizerotohero.com/errors/openssl-tlsv1-alert-unknown-ca/) and [How to set up mutual TLS](https://pkizerotohero.com/guides/mutual-tls-setup/).

### The connection is closed or reset during the handshake

```text
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to api.example.com:443
curl: (35) Recv failure: Connection reset by peer
```

The other side hung up without a TLS alert. Firewalls and proxies that filter by host name, IP allowlists, overloaded servers and TLS-inspecting devices are typical. **Confirm** with `curl -v` from another network, or without the proxy. **Fix:** ask the network or server owner to allow the connection; see [How to work behind a TLS-inspecting corporate proxy, safely](https://pkizerotohero.com/guides/corporate-proxy-certificates/).

Warning

\-k does not fix this, and is a trap anyway

`-k` (`--insecure`) only skips the certificate check, which code 35 never reaches. Adding it changes nothing here, but it often stays in the script and later hides a real attack. Fix the port, the proxy URL, the server’s TLS settings or the client certificate.

## Check yours

*Interactive tool: Paste your error (runs in the browser at [/tools/error-finder/](https://pkizerotohero.com/tools/error-finder/))*

Paste the whole curl line, including the text after `(35)`. The finder shows the page for that reason.

## Learn why

- [The TLS 1.3 handshake](https://pkizerotohero.com/handbook/tls-handshake/): which messages come first, and where each of these failures happens.
- The same failures elsewhere: [ERR\_SSL\_PROTOCOL\_ERROR](https://pkizerotohero.com/errors/chrome-err-ssl-protocol-error/) in Chrome and [WRONG\_VERSION\_NUMBER](https://pkizerotohero.com/errors/python-ssl-wrong-version-number/) in Python.

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/errors/curl-35-ssl-connect-error/).

Last reviewed 20 October 2026Edition 0.9

## Learn why it happens

- [Chapter 15: The TLS 1.3 handshake](https://pkizerotohero.com/handbook/tls-handshake/)

## Fix it step by step

- [How to configure TLS securely (versions, ciphers, HSTS)](https://pkizerotohero.com/guides/tls-configuration/)

## Everything on this topic

- [TLS and HTTPS](https://pkizerotohero.com/topics/tls/)
