Regular TLS asks “who are you?”
mTLS asks both sides.
Mutual TLS is how services prove their identity to each other — no API keys, no shared secrets, just certificates verified on every connection. This page teaches it the way engineers actually learn it: by watching the handshake happen and getting rejected without a cert.
The handshake, step by step
In one-way TLS, only the server proves who it is — your browser checks the site's certificate and that's the end of it. mTLS adds the second half: the server demands the same proof from the client. Step through it.
holds client.crt + client.key
holds server.crt + ca.crt
Get rejected. Then don't.
This is a simulation of a real mTLS-protected endpoint — the same behaviour you'd see against nginx, Envoy, or a Go server with RequireAndVerifyClientCert. Call it once bare, once with a client certificate, and watch both sides of the wire.
- Subject CN
- checkout-service
- SAN (SPIFFE ID)
- spiffe://mtls.ai/ns/prod/sa/checkout
- Issuer
- CN = mtls.ai demo CA
- Serial
- 04:A3:91:7C:22
- Valid
- 24 hours only — short-lived by design, rotated automatically
- Fingerprint
- 9F:2C:…:41:B7 (SHA-256, truncated)
The cert playground
Pick your stack, name your service, and take a working mTLS server config. Each one does the same three things: present a server cert, demand a client cert, and verify it against your CA before any request is served.
Assumes a CA at /etc/mtls/ca.crt and certs issued from it — generate them with the cheat sheet below. Client identity is passed to your app so it can authorise the caller, not just authenticate it.
The 2 a.m. cheat sheet
Every openssl command you actually need to stand up a demo CA and test an mTLS endpoint — the ones you end up Googling every single time.
AI agents need identity too
Here's the problem nobody's demo mentions: the moment AI agents start calling each other's APIs — a research agent invoking a payments agent invoking a database tool — API keys stop being an identity strategy. Keys leak, keys get shared, and a key tells you nothing about which workload is calling, only that someone once knew a secret.
mTLS already solved this for microservices. Every agent gets a short-lived certificate with a cryptographic identity — a SPIFFE ID like spiffe://mtls.ai/agent/research — issued automatically by the platform (SPIRE, cert-manager, or a cloud workload-identity service), rotated in hours, and verified on every single hop. No secrets in prompts. No keys in environment variables waiting to be exfiltrated by a prompt injection.
When an agent misbehaves, you revoke its identity and it stops existing on the network. Try doing that with a shared bearer token pasted into forty configs.
Identity before access
An agent proves what it is cryptographically before it's allowed to ask for anything — authentication from the transport layer, not an afterthought header.
Short-lived by default
Certificates measured in hours, not years. A stolen credential expires before it's useful, and rotation is the platform's job, not a calendar reminder.
Verify every hop
Agent-to-agent, agent-to-tool, tool-to-database: every connection is mutually authenticated, so a compromised caller can't move sideways unnoticed.