The MTLS Auth plugin lets you add mutual TLS authentication based on a client-supplied or a server-supplied certificate, and on the configured trusted certificate authority (CA) list.

Mutual TLS Authentication
How does the mTLS plugin work?
The mTLS plugin automatically maps certificates to Consumers based on the common name field. To authenticate a Consumer with mTLS, it must provide a valid certificate and complete a mutual TLS handshake with Kong Gateway.
The plugin validates the certificate provided against the configured CA list based on the requested Route or Service:
- If the certificate is not trusted, or expired, the response is
HTTP 401 TLS certificate failed verification
. - If no valid certificate is provided (including HTTP requests), the response is
HTTP 401 No required TLS certificate was sent
.
However, ifconfig.anonymous
is set, the request is allowed using the anonymous Consumer.
Client certificate request
Client certificates are requested during the ssl_certificate_by_lua
phase, where Kong Gateway doesn’t have access to Route or Workspace information.
Because of this, Kong Gateway requests a client certificate during every TLS handshake if the mtls-auth
plugin is configured on any Route or Service.
In most cases, if a client doesn’t present a certificate, it won’t affect proxying—unless the specific Route or Service requires mtls-auth
.
The main exception is desktop browsers, which may prompt users to select a certificate, potentially causing a confusing user experience even when the certificate isn’t needed.
To optimize TLS handshakes, Kong Gateway builds an in-memory map of SNIs from Routes that require client certificates.
This helps limit unnecessary certificate requests while ensuring mTLS is enforced when needed.
The map relies on Routes having the SNIs
attribute set.
If any Route lacks an SNI, Kong Gateway must request a client certificate during every TLS handshake.
Certificate request behavior based on plugin scope:
- Plugin applied globally: mTLS is enforced on every request across all Workspaces.
- Plugin applied at the Service level: If any associated Route lacks an SNI, mTLS is enforced on every request.
-
Plugin applied at the Route level:
- If any Route lacks an SNI, mTLS is enforced on every request.
- If all Routes have SNIs, mTLS is enforced only for matching SNI requests.
SNIs must be set for all Routes that mutual TLS authentication uses.
When using the plugin with expressions routes, the client certificate will always be requested, even if the routes are configured with SNIs.
Sending the CA DNs during TLS handshake
By default, Kong Gateway does not send the CA Distinguished Name (CA DN) list during the TLS handshake. Specifically, the certificate_authorities
field in the CertificateRequest
message is empty.
Some clients use the CA DN list to help select the correct certificate. To support this, set config.send_ca_dn
to true
.
This adds the CA certificates defined in config.ca_certificate
to the appropriate SNI entries.
As noted in Client certificate request, Kong Gateway does not have access to Route information during the ssl_certificate_by_lua
phase.
Instead, it builds an in-memory map of SNIs.
The CA DN list is linked to these SNIs, and if multiple mtls-auth
plugins with different config.ca_certificate
values are applied to the same SNI, their CA DNs are merged.
CA DN list association depends on plugin scope:
- Global scope: CA DNs are linked to a special SNI
*
. - Service level:
- CA DNs are associated with each SNI of the Service’s Routes.
- If a Route has no SNI, CA DNs are linked to
*
.
- Route level:
- CA DNs are associated with each SNI on the Route.
- If no SNI is configured, CA DNs are linked to
*
.
During the mTLS handshake:
- If the client includes a known SNI in the
ClientHello
, the corresponding CA DN list is sent in theCertificateRequest
. - If the client does not send an SNI or sends an unknown one, Kong Gateway only sends the CA DN list associated with
*
—and only if a client certificate is being requested.
Manual mappings between Certificate and Consumer objects
Sometimes, you might not want to use automatic Consumer lookup, or you have certificates that contain a field value not directly associated with Consumer objects. In those situations, you can manually assign one or more subject names to the Consumer entity for identifying the correct Consumer.
Note: Subject names refer to the certificate’s Subject Alternative Names (SAN) or Common Name (CN). CN is only used if the SAN extension does not exist.
You can create a Consumer mapping with either of the following:
- The
/consumers/{consumer}/mtls-auth
Admin API endpoint -
decK by specifying
mtls_auth_credentials
in the configuration like the following:consumers: - custom_id: my-consumer username: example-consumer mtls_auth_credentials: - id: bda09448-3b10-4da7-a83b-2a8ba6021f0c subject_name: test@example.com
The following table describes how Consumer mapping parameters work for the Mutual TLS Authentication plugin:
Form Parameter |
Default |
Description |
---|---|---|
id required for declarative config |
none | UUID of the Consumer mapping. Required if adding mapping using declarative configuration, otherwise generated automatically by Kong Gateway’s Admin API. |
subject_name required |
none |
The Subject Alternative Name (SAN) or Common Name (CN) that should be mapped to consumer (in order of lookup).
|
ca_certificate optional |
none |
|
Matching behaviors
After a client certificate has been verified as valid, the Consumer object is determined in the following order, unless config.skip_consumer_lookup
is set to true
:
- Manual mappings with
subject_name
matching the certificate’s SAN or CN (in that order) andca_certificate = {issuing authority of the client certificate}
. - Manual mappings with
subject_name
matching the certificate’s SAN or CN (in that order) andca_certificate = NULL
. - If
config.consumer_by
is not null, Consumer withusername
and/orid
matching the certificate’s SAN or CN (in that order). - The
config.anonymous
Consumer (if set).
Note: Matching stops as soon as the first successful match is found.
Upstream headers
When a client has been authenticated, the plugin appends some headers to the request before proxying it to the upstream service, so that you can identify the Consumer in your code:
-
X-Consumer-ID
: The ID of the Consumer in Kong Gateway. -
X-Consumer-Custom-ID
: Thecustom_id
of the Consumer (if set). -
X-Consumer-Username
: Theusername
of the Consumer (if set). -
X-Credential-Identifier
: The identifier of the credential (only if the Consumer is not theanonymous
Consumer). -
X-Anonymous-Consumer
: Is set totrue
if authentication fails, and theanonymous
Consumer is set instead.
You can use this information on your side to implement additional logic.
You can use the X-Consumer-ID
value to query the Admin API and retrieve
more information about the Consumer.
When config.skip_consumer_lookup
is set to true
, Consumer lookup is skipped and instead of appending aforementioned headers, the plugin appends the following two headers:
-
X-Client-Cert-Dn
: The distinguished name of the client certificate -
X-Client-Cert-San
: The SAN of the client certificate
Once config.skip_consumer_lookup
is applied, any client with a valid certificate can access the Service/API.
To restrict usage to only some of the authenticated users, also add the ACL plugin and create
allowed or denied groups of users using the same
certificate property being set in config.authenticated_group_by
.
Troubleshooting authentication failure
When authentication fails, the client doesn’t have access to any details that explain the failure. The security reason for this omission is to prevent malicious reconnaissance.
Instead, the details are recorded inside Kong Gateway’s error logs under the [mtls-auth]
filter.