v1.10.90-0e025b8
Skip to main content
SecurityTechnical

Mutual TLS for Proxy Authentication: When Client Certificates Beat Passwords

11 min read

By Hex Proxies Engineering Team

Mutual TLS for Proxy Authentication: When Client Certificates Beat Passwords

Most commercial proxy services authenticate clients by one of two means: a username and password sent in the HTTP CONNECT request, or an allowlist of source IPs. Both work. Neither is ideal for security-sensitive deployments. Mutual TLS (mTLS) offers a third option: the proxy presents a server certificate, the client presents a client certificate, and each side verifies the other's identity cryptographically before any bytes flow. This post covers how mTLS works for proxy authentication, when it is worth the operational overhead of running a certificate authority, and how it compares to the alternatives in practical terms.

How mTLS Differs from Regular TLS

Regular TLS authenticates only the server. The client verifies that the server's certificate chains to a trusted root and that the certificate is valid for the hostname it is connecting to. The server accepts any client that completes the handshake; it does not know or care who the client is at the TLS layer.

mTLS adds a mandatory client certificate step. During the handshake, the server sends a CertificateRequest message (RFC 8446, Section 4.3.2) specifying which certificate authorities it will accept. The client responds with a Certificate message containing its own chain and a CertificateVerify message signing the handshake transcript with its private key. The server verifies the chain against its trust store and verifies the signature against the client's public key. If either fails, the handshake aborts and no application data flows.

The practical effect is that the client's identity is bound to a private key that never leaves the client machine. Stealing a password is a matter of logging keystrokes or reading a config file. Stealing an mTLS client key requires extracting the key from whatever keystore holds it, which can be hardware-backed via TPM or Secure Enclave.

The PKI Overhead

mTLS requires a certificate authority. For small deployments, a single self-signed root that signs client certificates directly is sufficient. For larger deployments, an intermediate CA per environment or team is standard, following the same hierarchical patterns as public web PKI.

The operational components you need to stand up:

  1. Root CA key and certificate. Kept offline. Only used to sign intermediate CAs.
  2. Intermediate CA key and certificate. Used to sign client and server leaf certs. Kept in a hardware security module or, at minimum, on an air-gapped machine.
  3. Certificate issuance process. When a new client needs access, they generate a CSR, you sign it, and you return the leaf certificate.
  4. Revocation mechanism. Either a Certificate Revocation List (CRL) that the proxy fetches periodically, or OCSP for on-demand checks. Revocation is the hard problem.

Tools like step-ca (Smallstep), HashiCorp Vault's PKI engine, and cfssl from Cloudflare reduce the operational friction considerably. A step-ca instance can be running in under 30 minutes and handle automated certificate issuance via ACME.

Configuring a Proxy to Require mTLS

Here is a minimal nginx configuration that requires a client certificate:

server {
    listen 8443 ssl;
    server_name proxy.example.com;

    ssl_certificate         /etc/nginx/certs/proxy.crt;
    ssl_certificate_key     /etc/nginx/certs/proxy.key;
    ssl_client_certificate  /etc/nginx/certs/client-ca.crt;
    ssl_verify_client       on;
    ssl_verify_depth        2;

    location / {
        proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
        proxy_pass http://backend;
    }
}

The ssl_verify_client on directive makes the client certificate mandatory. The X-Client-CN header forwards the common name from the client certificate to the upstream, so backend services can make authorization decisions based on the authenticated identity. You can extract other fields too: $ssl_client_s_dn gives the full subject DN, $ssl_client_i_dn gives the issuer, and $ssl_client_fingerprint gives a SHA-1 fingerprint of the certificate.

Client-Side Configuration

A client using curl with mTLS looks like this:

curl --cert client.crt --key client.key \
     --cacert root-ca.crt \
     -x https://proxy.example.com:8443 \
     https://target.example/api

In Python with requests:

import requests
resp = requests.get(
    'https://target.example/api',
    proxies={'https': 'https://proxy.example.com:8443'},
    cert=('client.crt', 'client.key'),
    verify='root-ca.crt',
)

Note that the cert parameter here identifies the client to the proxy, not to the target. The target sees a regular TLS connection from the proxy.

Certificate Pinning

Beyond mTLS, some deployments pin the expected server certificate or its public key hash in the client configuration. The client refuses to connect if the server presents any certificate other than the pinned one, even if it would otherwise chain to a trusted root. Pinning defeats attackers who obtain a rogue certificate from a compromised public CA, but it also makes certificate rotation harder: every pin update requires a client update.

Pinning is best reserved for clients under your direct control (mobile apps, backend services) where you can ship a config update alongside a certificate rotation. For a general-purpose proxy service, pinning would require coordinating rotation with every customer simultaneously, which is usually not worth the risk reduction.

When to Use mTLS Versus Alternatives

The choice between authentication methods depends on threat model and operational capacity. A rough guide:

  • Username and password over HTTPS: adequate for most commercial price monitoring and SEO tracking use cases. Easy to rotate. Easy to leak.
  • IP allowlist: adequate when clients have stable public IPs (datacenter VMs, office VPN egress). Useless when clients are on DHCP or behind CGNAT.
  • mTLS: adequate and preferred for enterprise deployments where proxy access is sensitive, where clients can be issued certificates provisioned via MDM or device management, and where operators need auditable per-client identity without relying on password hygiene.
  • mTLS plus IP allowlist: defense in depth for highly sensitive deployments. An attacker must compromise both the client key and the network perimeter.

Revocation Is the Hard Problem

Issuing certificates is easy. Revoking them is not. When an employee leaves or a device is lost, you need the proxy to reject the associated certificate immediately. Three mechanisms exist:

  1. CRL (Certificate Revocation List). A file signed by the CA listing revoked serial numbers. The proxy fetches it periodically. Simple, but stale: a certificate revoked at 09:00 will continue to work until the CRL is next fetched, typically hourly.
  2. OCSP (Online Certificate Status Protocol). The proxy queries the CA for each incoming connection's certificate status. Real-time, but adds a network round trip to every handshake. Falls open if the OCSP responder is unreachable, which defeats the purpose.
  3. Short-lived certificates. Issue certificates valid for 24 hours or less. No revocation needed because nothing is valid long enough to matter. Requires automated renewal, which step-ca and similar tools handle via ACME. This is the modern best practice for mTLS deployments at scale.

A Real Deployment Note

In Hex Proxies internal testing of mTLS deployment for an enterprise customer in March 2026, the main operational friction was not the proxy or the CA. It was getting client certificates onto the customer's scraping infrastructure in an automated way. The customer ran 200+ worker containers across Kubernetes, and they needed each worker to have a unique certificate tied to its identity. The solution was a sidecar container that used Vault's Kubernetes auth method to request a short-lived certificate at pod startup, renew it in the background, and expose the cert and key via a shared volume. Total implementation time was roughly three engineering days. Once deployed, the customer stopped worrying about proxy password rotation entirely because there were no passwords.

Conclusion

mTLS is the strongest authentication option available for proxy access, and it is appropriate for enterprise deployments where the threat of credential theft is meaningful. The operational cost is running a certificate authority and handling renewal and revocation, which modern tooling has made tractable but not trivial. For small deployments with a handful of trusted clients, username and password authentication remains the right default. For anything involving customer data, compliance obligations, or access from untrusted networks, the case for mTLS is straightforward and the case against it is mostly inertia.