# curl: (60) SSL certificate problem: causes and fixes

> curl error 60 means curl could not verify the server's certificate chain. Fix the server chain or point curl at the right CA bundle instead of using -k.

Source: https://ssl.sh/errors/curl-60-ssl-certificate-problem/
Last updated: 2026-11-10

**`curl: (60) SSL certificate problem` means curl reached the server but could not build a trusted chain from the server's certificate to a root CA it trusts.** In most cases the server forgot to send its intermediate certificate; otherwise the client's CA bundle is missing, outdated, or does not contain your company's private root.

## Quick fix

A chain with one certificate means the server must be fixed; a full chain means the client's trust store is the problem.

Find out which side is broken before changing anything. Test the server from a clean environment:

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

- If the output shows only one certificate (`depth=0`) and ends with `Verify return code: 21 (unable to verify the first certificate)` or `20 (unable to get local issuer certificate)`, **the server chain is incomplete**. Install the full chain on the server (see [missing intermediate certificate](https://ssl.sh/errors/missing-intermediate-certificate/)).
- If the server sends a full chain and other machines connect fine, **your client's trust store is the problem**. Update it, or point curl at the right root:

```bash
curl --cacert /path/to/company-root.pem https://example.com
# or for every curl call in this shell
export CURL_CA_BUNDLE=/path/to/ca-bundle.pem
```

You can also paste the hostname into the [SSL checker](https://ssl.sh/tools/ssl-checker/) to see the chain the server sends.

## Causes

### 1. The server does not send its intermediate certificate

A server that omits its intermediate certificate is the most common cause. Web servers must send the leaf certificate plus every intermediate; only the root is left out. Browsers often hide the mistake, curl does not.

- **Confirm:** `openssl s_client -showcerts` lists a single certificate, and its `issuer` is an intermediate CA rather than a root.
- **Fix:** in Nginx point `ssl_certificate` at `fullchain.pem` (leaf followed by intermediates), not `cert.pem`. In Apache 2.4.8+ put the full chain in `SSLCertificateFile`. Rebuild the chain with the [chain builder](https://ssl.sh/tools/chain-builder/) if you only have the leaf.

### 2. The client CA bundle is missing or outdated

Minimal container images and old systems often ship without a CA bundle, or with one that predates a newer root.

- **Confirm:** `curl -v https://example.com 2>&1 | grep -i cafile` shows a path that does not exist, or the server's root is not in that file.
- **Fix:** install or update the system package (`ca-certificates`), or download Mozilla's bundle from curl.se and use it with `--cacert`.

### 3. A TLS-inspecting proxy or antivirus

Corporate proxies, security gateways and some antivirus products decrypt HTTPS and re-sign it with their own root. Machines that have that root in the OS store but not in curl's bundle fail with error 60.

- **Confirm:** the `issuer` in `openssl s_client` output names your proxy vendor instead of a public CA.
- **Fix:** get the proxy root from your IT team and add it to the system trust store (below) or pass it with `--cacert`.

### 4. Self-signed or private CA certificate

Internal services often use a self-signed certificate or an internal CA. The message then reads `self-signed certificate` or `self-signed certificate in certificate chain`.

- **Fix:** distribute the internal root and trust it explicitly. See [self-signed certificate in certificate chain](https://ssl.sh/errors/self-signed-certificate-in-certificate-chain/).

### 5. Hostname or date problems

If the message mentions `subject name does not match` or `certificate has expired`, curl verified the chain but the name or the validity period is wrong. Fix the certificate's [SAN](https://ssl.sh/glossary/san/) list or renew it.

## Fixes by environment

On every platform, fix the server's chain or add the missing root to the store that curl uses; never switch verification off.

**Debian / Ubuntu**

```bash
sudo apt-get install --reinstall ca-certificates
sudo cp company-root.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
```

**RHEL / Rocky / Fedora**

```bash
sudo cp company-root.pem /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
```

**Alpine and Docker images**

```dockerfile
RUN apk add --no-cache ca-certificates
COPY company-root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates
```

On Debian-based images use `apt-get install -y ca-certificates` instead.

**macOS:** the system curl uses the macOS keychain. Add the root in Keychain Access (System keychain, "Always Trust"). A Homebrew curl uses its own bundle; check `curl -V` and the `CAfile:` line from `curl -v`.

**Windows:** curl.exe shipped with Windows 10/11 uses Schannel and the Windows certificate store, so import the root with `certutil -addstore -f Root company-root.cer` from an elevated prompt.

**PHP, Git and other libcurl users** inherit the same problem; see [PHP cURL error 60](https://ssl.sh/errors/php-curl-error-60/) and [Git SSL certificate problem](https://ssl.sh/errors/git-ssl-certificate-problem/).

## Don't do this

- **`curl -k` / `--insecure` in scripts.** It turns off every certificate check, including the hostname check. A man-in-the-middle can then read API tokens and change downloads.
- **`echo insecure >> ~/.curlrc`.** This silently disables verification for every future curl call on that account.
- **Replacing the system bundle with a file copied from a forum.** Use the OS package or the bundle published on curl.se, and add private roots alongside it rather than instead of it.
