Machine Identity · mTLS

curl mTLS: Configure Mutual TLS from the Terminal

MUTUAL TLS — BOTH SIDES PROVE IDENTITYCLIENTcurl / serviceSERVERapi.example.compresents client certificate →← presents server certificateboth verified ✓

Mutual TLS has both client and server present certificates. With curl, use --cert, --key, and --cacert. Here is the full setup: generate certs, call an mTLS endpoint, and fix the three failures everyone hits.

By MachineCert EngineeringReviewed for technical accuracy by the MachineCert Security TeamUpdated July 24, 20268 min read
Answer first

Mutual TLS (mTLS) authenticates both ends of a connection: the server presents its certificate as usual, and the client presents one too. With curl you supply the client certificate and key with --cert and --key, and trust the server's CA with --cacert. This guide generates test certificates, makes a working mTLS request, and resolves the three errors people hit most: an untrusted server CA, a missing or wrong client certificate, and a hostname mismatch.

One-way TLS vs mutual TLS

Regular HTTPS authenticates one side: the server proves its identity to the client, and the client stays anonymous at the TLS layer. Mutual TLS closes the loop — the client presents a certificate too, and the server validates it before any request is processed. The hero diagram above shows the exchange: both ends present a certificate, both verify the other, and only then is the connection trusted. It's the default pattern for service-to-service traffic and machine identity, where "who is calling this API" has to be answered by cryptography, not a bearer token that can leak.

The three curl flags

bash
curl https://api.example.com/health \
  --cert   client.crt \    # the client certificate you present
  --key    client.key \    # its matching private key
  --cacert ca.crt          # the CA that signed the server's certificate
  • --cert / --key — your client identity (the client-side half of mTLS).
  • --cacert — the trust anchor for the server's certificate.

If the client cert and key are bundled in one PEM, --cert client.pem alone works; for a PKCS#12 bundle use --cert client.p12:password --cert-type P12.

Generate test certificates

To try mTLS locally you need a CA, a server cert, and a client cert — all chaining to that CA:

bash
# 1. a throwaway CA
openssl genrsa -out ca.key 2048
openssl req -x509 -new -key ca.key -days 365 -out ca.crt -subj "/CN=demo-ca"

# 2. a client key + certificate signed by the CA
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=test-client"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
    -CAcreateserial -out client.crt -days 90

Point your server at ca.crt as the client-CA trust store (in nginx: ssl_client_certificate ca.crt; ssl_verify_client on;), and the request above will succeed.

Verify it works

bash
# a 200 means the server accepted your client certificate
curl -sw "%{http_code}\n" -o /dev/null https://api.example.com/health \
  --cert client.crt --key client.key --cacert ca.crt

The three failures everyone hits

  • unable to get local issuer certificate — curl can't verify the server. Pass the server's CA with --cacert. Don't reach for -k/--insecure outside throwaway local tests; it disables verification entirely.
  • 400 No required SSL certificate was sent / alert certificate required — the server wanted a client certificate and didn't get a valid one. Confirm --cert/--key are set and the client cert is signed by the CA the server trusts.
  • SSL: no alternative certificate subject name matches — hostname mismatch. The name in the URL must match the server certificate's SAN. Fix the SAN or the URL; don't paper over it with --insecure.

mTLS setup checklist

  • Client presents --cert + matching --key; both readable, key permissions locked down (chmod 600)
  • Client trusts the server CA via --cacert — never -k/--insecure against a real endpoint
  • Server has ssl_verify_client on and trusts the client CA
  • Server and client certificates share (or chain to) a CA each side trusts
  • Certificate SANs match the hostnames actually used
  • Client certificates are short-lived and rotated — the same 47-day pressure applies to machine identities

Once the terminal call works, the same certificates drive mTLS in nginx, Envoy, and a service mesh — the handshake is identical; only the configuration surface changes.

Frequently asked

What do curl's --cert and --key do?

--cert points to the client certificate curl presents to the server, and --key points to the matching private key. Together they let the client prove its identity — the client-side half of mutual TLS. --cacert tells curl which CA to trust for the server's certificate.

How do I generate a client certificate for testing mTLS?

Create a CA, then issue a client key + certificate signed by it: openssl genrsa -out client.key 2048; openssl req -new -key client.key -out client.csr -subj '/CN=test-client'; openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 90. The server must be configured to trust the same CA.

Why do I get 'SSL certificate problem: unable to get local issuer certificate'?

curl can't build a trust chain to the server's certificate. Pass the server's CA with --cacert ca.crt. Only use -k / --insecure for throwaway local testing — it disables verification entirely and must never be used against real endpoints.

Is mTLS different from normal HTTPS?

Normal HTTPS (one-way TLS) authenticates only the server to the client. mTLS adds the reverse: the client also presents a certificate the server validates, so both ends prove identity before any data flows. It's the common pattern for service-to-service and machine identity.

References & Standards
  1. RFC 8446 — The Transport Layer Security (TLS) Protocol Version 1.3, IETF (client authentication).
  2. RFC 5280 — Internet X.509 Public Key Infrastructure Certificate and CRL Profile, IETF.
See it in practice

Stop tracking certificates by hand.

MachineCert discovers every certificate across your clouds and internal networks, scores each for expiry risk and automation readiness, and automates renewal — then verifies the new certificate is actually serving.

Run a free certificate scanSee machine identity
  • No signup
  • About 60 seconds
  • Public certificates only