Fix TLSV1_ALERT_UNKNOWN_CA: Solving "curl (35) OpenSSL SSL_connect: alert unknown ca"

intermediate🔒 SSL/TLS2026-05-29| Linux (Ubuntu/CentOS), macOS, Docker, Nginx, Apache

Error Message

curl: (35) OpenSSL SSL_connect: alert unknown ca:s:/C=US/O=.../CN=...
#ssl#tls#certificate-authority#curl#openssl#mtls

The 60-Second Fix

When a TLS handshake fails with alert unknown ca, the receiver is essentially saying: "I see your certificate, but I have no idea who vouched for it." This usually happens when the machine receiving the certificate doesn't recognize the Certificate Authority (CA) that signed it.

You will likely encounter this during mTLS (Mutual TLS) setups or when using self-signed certificates for internal APIs.

  • On the client side: Pass your CA bundle explicitly using --cacert or install the CA into your system's trust store.
  • On the server side (mTLS): Verify that your Nginx ssl_client_certificate or Apache SSLCACertificateFile points to the actual Root CA that signed the user certificates.
  • Testing only: Use curl -k to bypass the check, but never do this in production.

Why Is Your Connection Failing?

If you see the error message curl: (35) OpenSSL SSL_connect: alert unknown ca, the handshake hit a wall. One peer sent a "Fatal Alert" to the other. Specifically, unknown_ca means the certificate was received, but the chain of trust is broken because the Root CA is missing from the local trust store.

In standard HTTPS, your browser or curl validates the server. However, in an mTLS setup—common for high-security APIs on port 8443—both sides must verify each other. If you get this error while sending a client certificate, the server is the one rejecting you because it doesn't recognize your issuer.

Real-World Fixes for Common Scenarios

1. Updating the System Trust Store (Linux)

Internal tools often use private CAs. If your local machine doesn't trust your company’s internal CA, you must add it manually to prevent OpenSSL from throwing a fit.

Ubuntu/Debian:

# Move your CA file to the trusted folder
sudo cp internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt

# Rebuild the certificate store
sudo update-ca-certificates

CentOS/RHEL 7 & 8:

# Copy the file to the anchor directory
sudo cp internal-ca.crt /etc/pki/ca-trust/source/anchors/

# Update the system trust
sudo update-ca-trust extract

2. Fixing mTLS Server Configurations

Are you the admin? If your clients are seeing this alert, your server probably lacks the bundle needed to verify their identities. Even a valid client certificate is useless if the server doesn't have the signer's Root CA on file.

Nginx Setup:

server {
    listen 443 ssl;
    ssl_certificate /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;

    # Crucial: This must be the CA that signed your CLIENT certs
    ssl_client_certificate /etc/nginx/certs/client-ca.crt;
    ssl_verify_client on;
}

Apache Setup:

<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile /etc/apache2/ssl/server.crt
    SSLCertificateKeyFile /etc/apache2/ssl/server.key

    # Points to the CA allowed to sign client certs
    SSLCACertificateFile /etc/apache2/ssl/client-ca.crt
    SSLVerifyClient require
</VirtualHost>

3. Providing CA Bundles Directly to Curl

Modifying system-wide stores isn't always possible, especially in locked-down CI/CD pipelines. Instead, point curl directly to the file it needs. For standard connections, use:

curl --cacert path/to/ca-bundle.crt https://api.internal.dev

For mTLS connections, you need to provide your certificate, your private key, and the CA that validates the server:

curl --cacert server-ca.crt \
     --cert client.crt \
     --key client.key \
     https://mtls.example.com:8443

Deep Diagnostics with OpenSSL

Curl's output can be vague. To see exactly where the handshake dies, use the s_client tool. It provides a verbose look at the certificate exchange.

openssl s_client -connect your-server.com:443 -CAfile my-ca.crt

Scan the output for the Verification status:

  • Verification: OK: Everything is working perfectly.
  • self signed certificate in certificate chain: You are likely missing the Root CA in your store.
  • unable to get local issuer certificate: An intermediate CA is missing. You need the full chain.

Common Pitfalls to Avoid

  • The Intermediate Gap: Many people forget that certificates are often signed by an Intermediate CA, not the Root directly. Ensure your CA bundle includes the full chain (Intermediate + Root).
  • Format Issues: OpenSSL prefers PEM (Base64). If your CA is a binary .cer or .der file, convert it: openssl x509 -inform der -in cert.cer -out cert.pem.
  • Permission Denied: Ensure the user running your web server (like www-data or nginx) actually has read permissions for the .crt and .key files. A chmod 644 on certificates is a safe bet.

Related Error Notes