Client mTLS Authentication
Enterprise Feature
Client mTLS is available as an add-on as part of an enterprise plan. If you would like to purchase this feature, please contact us at sales@zuplo.com or reach out to your account manager.
Most enterprise features can be used in a trial mode for a limited time. Feel free to use enterprise features for development and testing purposes.
Client mTLS authentication lets your Zuplo gateway verify the identity of clients calling your API using certificates issued by your own Certificate Authority (CA). Both the client and the gateway authenticate each other during the TLS handshake, so only clients holding a certificate signed by your CA can reach your API.
How Client mTLS Works
When a client calls your Zuplo gateway:
- The client presents a certificate issued by your CA during the TLS handshake.
- Zuplo's edge verifies the certificate against the CA you've uploaded and passes the verification result (and parsed certificate) to your gateway workers.
- The
mtls-auth-inboundpolicy on your route reads the verification result, enforces it, and attaches the parsed certificate metadata torequest.user.data.mtlsAuthfor use in your handlers and downstream policies.
CA certificates are scoped to your Zuplo account, not a single project or deployment. Once a CA is uploaded, every gateway domain on the account will verify presented client certificates against it. The policy on each route controls whether unverified traffic is rejected or allowed through.
Prerequisites
Before you begin, you need:
- A public CA certificate (PEM-encoded) that issued — or will issue — the client certificates you want to accept
- The Zuplo CLI installed and authenticated
- A Zuplo project where you can add the
mtls-auth-inboundpolicy to a route
You only upload your public CA certificate to Zuplo. Private keys and issued client certificates stay with you and your clients.
1/ Upload Your CA Certificate
Use the Zuplo CLI to upload your CA certificate. The CA is registered against your account and is automatically made available on all of your gateway domains.
First, authenticate your client:
Code
Then you can create a CA by running:
Code
Parameters:
--name: A unique identifier for the CA. Must be a valid JavaScript identifier (letters, digits,_,$; cannot start with a digit).--cert: Path to the PEM-encoded CA certificate (-----BEGIN CERTIFICATE-----...). DER is not supported.--account: Your Zuplo account name.
The command returns the new CA's ID (prefixed with mtlsca_). You can list all
CAs on the account at any time:
Code
See the ca-certificate CLI reference for
all available subcommands (create, list, describe, update, delete).
Using an intermediate CA
If your client certificates are issued by an intermediate CA (rather than directly by your root), upload the intermediate itself as the CA — not the root. The client certs used must be directly signed by the CA certificate you provide to Zuplo.
2/ Add the mTLS Auth Inbound Policy
Add the mtls-auth-inbound policy to any
route that should require a verified client certificate. The policy reads the
verification result that Zuplo's edge attached to the request and either rejects
unverified traffic or allows it through, depending on configuration.
config/policies.json
Key options:
allowUnauthenticatedRequests(defaultfalse): Whenfalse, the policy rejects requests that don't present a valid client certificate signed by a CA on your account. Whentrue, the policy lets traffic through but still attaches certificate metadata when a parseable client certificate is present — useful for staged rollouts or logging-only modes.certIssuerDN: The fully qualified issuer distinguished name that the client certificate must be signed by.
See the full policy reference for all options.
Finding your certIssuerDN value
The issuer DN of a client certificate is the subject DN of the CA that signed
it. Read it directly from your CA's PEM file with openssl:
Code
This prints something like subject=CN=example-ca,O=Example,C=US. Copy the part
after subject= into certIssuerDN. The policy tolerates casing and whitespace
differences, but not RDN reordering, so keep the order produced by openssl
as-is.
3/ Read Certificate Metadata in Your Handler
When verification succeeds, the policy attaches parsed certificate metadata to
request.user.data.mtlsAuth. If request.user does not already exist, the
policy also sets request.user.sub to the certificate subject.
Code
The metadata object includes:
subject— the client certificate subject DNissuer— the issuer DN (the CA that signed the certificate)notBefore/notAfter— validity window in ISO 8601 formatsha256Fingerprint— SHA-256 digest of the DER-encoded certificate, uppercase hex with colon separators (e.g.AB:CD:EF:...). Useful for pinning specific client certificates.
The raw client certificate is also available on
context.incomingRequestProperties.clientCert in
RFC 9440 format
(Base64-encoded DER, colon-wrapped) if you need to perform custom parsing or
forward it to a backend.
Managing CA Certificates
Listing CAs
Code
Inspecting a CA
Code
Renaming a CA
Only the name can be updated; to replace the certificate body, delete the CA and create a new one.
Code
Deleting a CA
Code
Deleting a CA stops verification for client certificates issued by it on all of
your gateway domains. Routes that use mtls-auth-inbound with
allowUnauthenticatedRequests: false will start rejecting those clients
immediately. Rotate to a new CA and update your clients before deleting the old
CA.
Rotating a CA
To rotate the CA without downtime:
- Upload the new CA alongside the existing one with
zuplo ca-certificate create. - Reissue client certificates from the new CA and distribute them to your clients.
- Once all clients have moved to the new CA, delete the old CA with
zuplo ca-certificate delete.
If you've followed the common practice of preserving the CA's subject DN across
the rotation (only the key, serial, and validity dates change), the issuer DN on
newly issued client certificates is identical to the previous one and
certIssuerDN does not need to change. If the rotation deliberately changes
the CA's subject DN, update certIssuerDN to match the new value before cutting
clients over — or temporarily set allowUnauthenticatedRequests: true to allow
both issuers during the transition.
Local Development
The mtls-auth-inbound policy relies on verification metadata supplied by
Zuplo's edge proxy and does not work in local development with zuplo dev. Test
the policy in a working-copy or preview environment.
Troubleshooting
Requests are rejected with 401
- Confirm the client is presenting a certificate signed by a CA that's been
uploaded with
zuplo ca-certificate list. - If you've set
certIssuerDN, verify it matchesrequest.user.data.mtlsAuth.issuerexactly (casing and whitespace are tolerated, but RDN order is not). - Temporarily set
allowUnauthenticatedRequests: trueand logcontext.incomingRequestProperties.clientMtlsVerificationStatusandcontext.incomingRequestProperties.clientMtlsVerificationReasonto see why verification failed.
request.user.data.mtlsAuth is missing
- The policy only attaches metadata when a parseable client certificate is present on the request. Confirm the client is sending one.
- Verify the route includes the
mtls-auth-inboundpolicy.
Custom domains
When a CA is uploaded, it's automatically associated with Zuplo's managed gateway domains. A custom domain must be active in the dashboard (check the Settings/Custom Domains sidebar) before CA verification will become active on that custom domain.
If you use a custom domain and your clients aren't being verified against it, contact support@zuplo.com.
Additional Resources
mtls-auth-inboundpolicy referenceca-certificateCLI reference- Gateway to Origin mTLS Authentication — the reverse direction, where Zuplo authenticates to your backend with a client certificate
If you need help configuring client mTLS for your account, contact us at support@zuplo.com.