A New Collection of Thoughtful Learning Apps — Now Available on iOS & Android

Image
I’m excited to share a set of mobile apps I’ve recently completed and published on both the Google Play Store and the Apple App Store. These apps are designed with a simple goal in mind: to make meaningful, structured content more accessible, whether you’re studying theology or improving your English vocabulary. 📱 Now Available on Both Platforms All apps are live and available for download: Google Play Developer Page: https://play.google.com/store/apps/dev?id=5835943159853189043 Apple App Store Developer Page: https://apps.apple.com/ca/developer/q-z-l-corp/id1888794100 📖 Theology & Confession Study Apps For those interested in Reformed theology and classical Christian teachings, I’ve developed a series of apps that present foundational texts in a clean, focused reading format: The Belgic Confession Canons of Dort Heidelberg Catechism Westminster Shorter Catechism Each app is designed to provide a distraction-free experience, making it easier to read, reflect, and revisit these im...

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

2026 Begins: Choosing to Stay on the Path as a Blogger

Health Checks and Scaling Strategies for Next.js in Kubernetes