Mutual TLS

Mutual TLS is ordinary TLS with one addition — the client also presents a certificate, so both ends authenticate rather than just the server. The server is configured to request and require a client certificate, and to trust a specific CA for them; the client is configured with its own certificate and key. The client certificate needs `clientAuth` in its extended key usage, and the CA that issues client certificates should be a private one you control, not a public CA.

Updated August 19, 2026 Verified against OpenSSL 3.5.7

What actually changes #

An ordinary TLS handshake authenticates one side. In mTLS the server sends a CertificateRequest and the client answers with a certificate and a signature proving it holds the matching key.

Ordinary TLS                  Mutual TLS
------------                  ----------
Server proves identity        Server proves identity
Client verifies it            Client verifies it
                            Server requests a certificate
                            Client proves identity
                            Server verifies it against its client CA

The three pieces you need #

  1. A CA for client certificates. Private, and separate from whatever issues your server certificates. See create a root CA.
  2. A client certificate and key, issued by that CA, carrying clientAuth in its extended key usage.
  3. Server configuration telling it to request client certificates and which CA to trust for them.

Server configuration #

ssl_client_certificate /etc/ssl/certs/client-ca.crt;
ssl_verify_client       on;      # 'optional' to roll out gradually
ssl_verify_depth        2;

# Pass the identity to the application.
proxy_set_header X-Client-DN     $ssl_client_s_dn;
proxy_set_header X-Client-Verify $ssl_client_verify;
SSLCACertificateFile /etc/ssl/certs/client-ca.crt
SSLVerifyClient      require
SSLVerifyDepth       2
SSLOptions           +StdEnvVars +ExportCertData
bind :443 ssl crt /etc/haproxy/certs/site.pem \
   ca-file /etc/haproxy/certs/client-ca.crt verify required

Testing it #

With a client certificate
openssl s_client -connect example.com:443 -servername example.com \
-cert client.crt -key client.key </dev/null
curl equivalent
curl --cert client.crt --key client.key https://example.com/

Without one, a server set to require closes the connection during the handshake — an abrupt failure that does not look like an authentication error, which is worth knowing before you debug it.

What usually breaks #

Symptom Cause
Handshake fails with no useful message Server requires a certificate, client sent none
unsupported certificate purpose Client certificate lacks clientAuth in its EKU
Server rejects a valid-looking certificate Issued by a CA the server does not trust for clients
Works with curl, fails from the application The runtime uses a different key or trust store
Fails after renewal New client certificate issued by a different CA

Check the extended key usage first — it is the most common single cause:

openssl x509 -in certificate.crt -noout -ext extendedKeyUsage
Expected output
X509v3 Extended Key Usage:
  TLS Web Server Authentication, TLS Web Client Authentication

See key usage vs extended key usage, and issuing a client certificate.

Frequently asked questions

Can I use a public CA for client certificates?
You should not. Trusting a public CA for client authentication means every certificate that CA has ever issued — millions of them — becomes a valid credential for your service. Use a private CA whose only purpose is issuing your client certificates.
What is the difference between "request" and "require"?
Requesting asks the client for a certificate and continues without one if it is not offered, which lets you roll mTLS out gradually. Requiring rejects the handshake outright. Most deployments start with request and switch to require once every client is issued.
Does mTLS replace application-level authentication?
It can, but it answers a different question. A certificate proves which machine or service is connecting; it says nothing about which user is acting. Many systems use both.
How do I revoke one client?
Publish a CRL your server checks, or re-issue the CA with a new trust list. This is the main operational cost of mTLS, and the reason client certificates are usually issued with short lifetimes.

References