Error message
SSL_do_handshake() failed (SSL: error:0A000418:SSL routines::tlsv1 alert unknown ca:SSL alert number 48)
Also printed as
- SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca
- error:0A000418:SSL routines::tlsv1 alert unknown ca
nginx logs it at info level as "SSL_do_handshake() failed (SSL: error:0A000418:SSL routines::tlsv1 alert unknown ca:SSL alert number 48) while SSL handshaking, client: …". The openssl CLI prints "…:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:…". OpenSSL clients send this alert for untrusted, self-signed and incomplete chains alike.
nginx error log / OpenSSL — TLS alert 48 (unknown_ca) received from the peer
What it means
The other side of the connection aborted the handshake with TLS alert 48 (unknown_ca): it could not chain the certificate it received to a CA it trusts. In a server log (as here, from nginx) it means a client refused the server's certificate. On a client it means the server refused the client certificate in mutual TLS. The side that logs the alert is never the side that has the problem: the cause lies with the certificate sent to the peer.
Why it happens
Server log: clients do not trust the server's certificate
commonSelf-signed or private-CA certificates, a missing intermediate, or a certificate meant for internal use exposed to public clients. Each failing client (curl, API clients, monitoring) leaves one such line.
Client side: the server does not trust your client certificate
commonIn mutual TLS, the client certificate comes from a CA the server is not configured to accept, or the client omits its intermediate.
Scanners and probes
occasionalInternet-wide scanners connect without trusting anything and abort with this alert; isolated lines from unknown addresses are harmless.
How to fix it
- 1.
Work out which certificate was rejected
In a server log, test the server's chain from outside; on a client running mutual TLS, check the client certificate against the CA the server expects.
bash # In a SERVER log: a client rejected the server's certificate (e.g. missing intermediate) openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:|Verify return code' # On a CLIENT: the server rejected your client certificate (mutual TLS) - 2.
Server: serve a complete, publicly trusted chain
For public endpoints, use a certificate from a public CA and configure the full chain. For internal endpoints, make sure every client has the internal root.
text # nginx: the chain clients need, leaf first ssl_certificate /etc/nginx/tls/fullchain.pem; ssl_certificate_key /etc/nginx/tls/privkey.pem; - 3.
Mutual TLS: issue client certificates from the trusted CA
Verify the client certificate against the CA the server is configured with (ssl_client_certificate in nginx), and send the client's intermediate along with its certificate.
bash # Mutual TLS: is the client certificate issued by the CA the server trusts? openssl verify -CAfile server-trusted-client-ca.pem -untrusted client-intermediate.pem client.pem # Send the client's intermediate too, if it has one cat client.pem client-intermediate.pem > client-chain.pem curl --cert client-chain.pem --key client.key https://mtls.example.com/
Related errors
SSL routines:ssl3_read_bytes:ssl/tls alert bad certificateOpenSSLSSL routines:ssl3_read_bytes:tlsv13 alert certificate requiredOpenSSL (client side)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)remote error: tls: unknown certificate authorityGo crypto/tls