# Client auth EKU removed: mTLS fails with public certificates

> Public TLS certificates no longer carry the clientAuth EKU, so mTLS with a Let's Encrypt or other public cert fails. Why, and how to move to a private CA.

Source: https://ssl.sh/errors/client-auth-eku-removed/
Last updated: 2026-11-10

**This error means a server that requires a client certificate received a certificate whose [Extended Key Usage](https://ssl.sh/glossary/extended-key-usage/) allows only server authentication.** Since 2026 public CAs such as Let's Encrypt no longer put the TLS Client Authentication EKU into TLS certificates, so any [mutual TLS](https://ssl.sh/glossary/mtls/) setup that used a public certificate on the client side fails after renewal.

## Quick fix

A client certificate that lists only TLS Web Server Authentication confirms the problem.

Check the EKU of the certificate the client presents, with the command below or the [certificate decoder](https://ssl.sh/tools/certificate-decoder/):

```bash
openssl x509 -in client.pem -noout -ext extendedKeyUsage
```

If the output shows only `TLS Web Server Authentication`, the missing clientAuth EKU is your problem. Issue a client certificate from a private CA instead and configure the server to trust that CA:

```bash
# one-time: create a private CA (keep ca.key offline or in an HSM/KMS)
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
  -keyout ca.key -out ca.pem -days 3650 -subj "/CN=Example Internal Client CA"

# per client: key, CSR, and a certificate with clientAuth
openssl req -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
  -keyout client.key -out client.csr -subj "/CN=billing-service"
printf "extendedKeyUsage=clientAuth\nkeyUsage=digitalSignature\n" > client.ext
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial \
  -out client.pem -days 90 -extfile client.ext
```

```nginx
ssl_client_certificate /etc/nginx/client-ca.pem;   # private CA only
ssl_verify_client on;
```

## Causes

### 1. Your public certificate lost clientAuth at renewal

Most cases start at renewal: the new public certificate no longer includes clientAuth.

- **What changed:** the Chrome Root Program Policy (v1.8) makes public CAs split server and client authentication into separate hierarchies. Subordinate CA certificates disclosed to CCADB on or after 15 June 2026 must carry only the serverAuth EKU, and subscriber certificates issued on or after 15 March 2027 must include an EKU that asserts only serverAuth. CAs removed clientAuth earlier on their own schedules: Let's Encrypt removed clientAuth from its default `classic` profile on 11 February 2026, kept an opt-in `tlsclient` profile until 8 July 2026, and its new Generation Y certificates carry server authentication only.
- **Confirm:** the failing certificate is from a public CA, was issued recently, and the EKU check above shows only server authentication.
- **Fix:** move the client side to a private CA as shown above.

### 2. Server-to-server mutual authentication

Some setups reuse the same certificate in both directions: a service presents its web certificate when it calls another service, an XMPP or mail server presents its server certificate on outgoing connections, or replication peers authenticate each other with public certificates.

- **Confirm:** the error appears in the logs of the receiving server, and the sending server uses its normal public certificate as its client identity.
- **Fix:** give each peer a second certificate from a private CA for the client role, or switch that link to a different authentication method (shared secret, OAuth 2.0 client credentials, signed tokens) if the protocol allows it.

### 3. The private CA certificate lacks the right EKU

If you already use a private CA and still see error 26, the client certificate or an intermediate CA was issued without clientAuth.

- **Confirm:** run the EKU check on the leaf and on each intermediate; an intermediate with an EKU extension must also include clientAuth.
- **Fix:** reissue with `extendedKeyUsage=clientAuth`.

## Fixes by environment

On every server, issue client certificates from your own private CA and trust only that CA for client authentication.

**Nginx:** set `ssl_verify_client on` and point `ssl_client_certificate` at the private CA bundle only. Remove public roots from that file.

**Apache httpd:** set `SSLVerifyClient require` and `SSLCACertificateFile /etc/httpd/client-ca.pem`.

**Java clients:** load the private client key pair into a PKCS#12 keystore (`-Djavax.net.ssl.keyStore=client.p12`). A serverAuth-only certificate is rejected with a message such as "Extended key usage does not permit use for TLS client authentication".

**Go:** a server using `tls.RequireAndVerifyClientCert` rejects the certificate with `x509: certificate specifies an incompatible key usage`. Set `ClientCAs` to a pool containing only the private CA.

**Kubernetes and service meshes:** use cert-manager with a CA or Vault issuer for workload certificates; meshes such as Istio and Linkerd already issue mTLS certificates from their own private CA.

**Private CA software:** options include smallstep step-ca, HashiCorp Vault PKI, cloud private CA services and, for small setups, plain OpenSSL. Automate issuance and renewal the same way you automate public certificates.

## Don't do this

- **Disable the purpose check** (for example by forcing OpenSSL's purpose to "any" or patching the verifier). The server would then accept certificates that were never meant to identify clients.
- **Set `ssl_verify_client optional_no_ca`** to make the error disappear. Nginx then accepts any client certificate without verifying who issued it, which removes the authentication you wanted.
- **Trust a public root store for client certificates.** Any certificate holder of that CA could authenticate. Keep the client trust anchor private.
- **Look for a CA that still mixes server and client EKUs to keep the old setup.** Even if one exists today, Chrome Root Program Policy requires subscriber certificates issued from 15 March 2027 to assert only serverAuth, so the option has a fixed end date.
