Debugging Node.js mTLS Client: “Self-signed certificate in certificate chain”

Debugging Node.js mTLS Client: “Self-signed certificate in certificate chain”



When making an API request from a Node.js client to a server that enforces mutual TLS (mTLS), you might encounter this error:

[cause]: Error: self-signed certificate in certificate chain
    at TLSSocket.onConnectSecure (node:_tls_wrap:1679:34)
    at TLSSocket.emit (node:events:518:28)
    at TLSSocket._finishInit (node:_tls_wrap:1078:8)
    at ssl.onhandshakedone (node:_tls_wrap:864:12) {
  code: 'SELF_SIGNED_CERT_IN_CHAIN'
}

Understanding the Issue

This error typically occurs when the client cannot fully verify the server’s certificate chain. In mTLS, both client and server need to trust each other’s root CA certificates. If any root in the chain is missing from the client’s CA bundle, the handshake fails.

Step 1: Inspect the Server Certificate Chain

You can use OpenSSL to see the full server chain:

openssl s_client -connect server.example.com:443 -showcerts

This will display all certificates sent by the server, including intermediate and root certificates. Check if the root CA is included in your client ca.pem file.

Step 2: Enable Node.js TLS Debug Logging

To get more insight into the TLS handshake in Node.js, enable debug logs:

NODE_DEBUG=tls,https,net node app.js

This can help identify which certificate is causing the verification failure.

Step 3: Verify mTLS Requirements

  • Both client and server must trust each other’s root CA chains.
  • If any root certificate is missing, the TLS handshake fails.
  • This applies to Node.js clients as well as Spring Boot or other server-side clients.

Step 4: Apply the Fix

In my case, the client CA bundle (ca.pem) was missing one root CA from the server chain. Adding that root certificate resolved the handshake issue immediately:

cat server_root_ca.pem >> ca.pem

After updating the CA bundle, the Node.js client successfully established an mTLS connection with the server.

Key Takeaways

  1. Always verify the server certificate chain with OpenSSL before troubleshooting mTLS issues.
  2. Enable NODE_DEBUG=tls,https,net for detailed handshake logs in Node.js.
  3. Ensure that both client and server trust each other’s root CAs for mTLS.
  4. This debugging pattern works for other clients, including Spring Boot apps.

Following these steps can save hours of frustration when dealing with self-signed certificate errors in mTLS environments.

❤️ Support This Blog


If this post helped you, you can support my writing with a small donation. Thank you for reading.


Comments

Popular posts from this blog

fixed: embedded-redis: Unable to run on macOS Sonoma

Copying MDC Context Map in Web Clients: A Comprehensive Guide

Reset user password for your own Ghost blog