Mutual TLS requires both the server AND the client to present valid certificates, authenticating both parties rather than just the server (as in standard HTTPS). Common for securing service-to-service or API access.
Standard TLS vs Mutual TLS
Standard HTTPS only verifies the server's identity to the client — mTLS additionally requires the client to present a valid certificate, verified by the server, providing strong authentication beyond passwords or API keys alone.
Common Use Cases
- Service-to-service communication in a microservices architecture
- API access for trusted partner integrations
- Internal admin/management interfaces requiring strong authentication
Step 1 — Create a Certificate Authority for Client Certificates
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/CN=My Internal CA"
This CA will sign client certificates — keep ca.key highly secure, since it's the root of trust for all client authentication.
Step 2 — Generate a Client Certificate
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=client1"
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt
Step 3 — Configure Nginx to Require Client Certificates
server {
listen 443 ssl;
server_name api.yourdomain.com;
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_verify_client on;
}
ssl_client_certificate points to your CA certificate (used to verify client certs); ssl_verify_client on makes presenting a valid client certificate mandatory.
Step 4 — Test with curl Using the Client Certificate
curl --cert client.crt --key client.key https://api.yourdomain.com
Step 5 — Verify Requests Without a Client Certificate Are Rejected
curl https://api.yourdomain.com
Should fail with a TLS handshake error, confirming mTLS is actually being enforced.
Accessing Client Certificate Details in Your Application
proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn;
Pass the verified client certificate's identity to your backend application via a header, letting the application know which specific client authenticated, useful for authorization logic.
Making Client Verification Optional (Instead of Mandatory)
ssl_verify_client optional;
Useful if you want to support both mTLS-authenticated and standard requests on the same endpoint, checking within your application whether a valid client certificate was presented.
Certificate Rotation and Revocation
Plan for client certificate expiration and rotation — unlike server certificates renewed via Let's Encrypt automation, client certificates in an mTLS setup typically need a deliberate process for issuing, distributing, and revoking them as your set of trusted clients changes.
Common Errors
"400 Bad Request: No required SSL certificate was sent" — the client didn't present a certificate; verify the client is correctly configured to send one (as shown in the curl example).
Valid client certificate still rejected — verify the client certificate was actually signed by the CA specified in ssl_client_certificate, and that it hasn't expired.
Continue Reading
- How to Build and Secure a REST API on a VPS
- How to Set Up API Authentication with JWT
- Understanding Certificate Chains and Intermediate Certificates
Browse more articles in SSL/TLS & Certificates.