1
0
mirror of https://github.com/keycloak/keycloak.git synced 2026-10-23 19:29:23 +03:00
Files
keycloak/docs/guides/server/reverseproxy.adoc
Alexander Schwartz 1138b6d331 Fix broken AsciiDoc formatting and linking
Closes #54128

Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
2026-10-20 16:31:21 +02:00

34 KiB
Raw Permalink Blame History

<#import "/templates/guide.adoc" as tmpl> <#import "/templates/kc.adoc" as kc> <#import "/templates/options.adoc" as opts> <#import "/templates/links.adoc" as links>

<@tmpl.guide title="Configuring a reverse proxy" summary="Configure {project_name} with a reverse proxy, API gateway, or load balancer."

includedOptions="proxy-* hostname hostname-admin http-relative-path shutdown-delay shutdown-timeout">

Distributed environments frequently require the use of a reverse proxy. {project_name} offers several options to securely integrate with such environments.

TLS termination modes

When deploying {project_name} behind a reverse proxy, there are three common approaches for handling TLS connections between clients, the proxy, and {project_name}: re-encrypt, edge termination and passthrough.

Re-encrypt

For re-encrypt, the reverse proxy terminates the client TLS connection and establishes a TLS connection to {project_name}. The proxy can inspect and modify HTTP headers (such as Forwarded or X-Forwarded-*) before re-encrypting the traffic to the backend. The proxy and {project_name} use independent TLS certificates: the proxy presents its own certificate to clients, while {project_name} uses a separate certificate for backend communication. Configure --proxy-headers so that {project_name} parses the forwarded headers set by the proxy, including the real client IP address.

Edge termination

The edge termination is similar to re-encrypt as the proxy terminates the TLS connection, but the connection between the proxy and {project_name} is then unencrypted. As edge termination is generally considered less secure than re-encrypt, this {section} focuses on re-encrypt instead.

Passthrough

With passthrough, the reverse proxy forwards the raw TLS connection directly to {project_name} without terminating it. The TLS handshake occurs between the client and {project_name}, so the proxy cannot inspect or modify HTTP traffic. Do not set --proxy-headers because the proxy cannot inject forwarded headers into the encrypted traffic. Enable the PROXY protocol (--proxy-protocol-enabled=true) so that {project_name} can see the real client IP address.

Comparison

The following table compares different aspects of the re-encrypt and passthrough proxy configurations.

Aspect Re-encrypt Passthrough

TLS between client and proxy

Yes

Yes (end-to-end)

TLS between proxy and {project_name}

Yes

N/A (direct tunnel)

Proxy can modify HTTP headers

Yes

No

Proxy can filter URL paths

Yes

No

{project_name} manages TLS certificates

Yes

Yes

Client certificate (X.509) forwarding

Via HTTP headers added by the proxy

Native (no header needed)

When to use each mode

Re-encrypt is the most common choice for production deployments. It allows the proxy to set forwarded headers, filter URL paths, and apply HTTP-level policies while maintaining TLS encryption on both sides of the proxy. The proxy and {project_name} can use different TLS certificates, giving you flexibility in certificate management. Because the proxy can inspect HTTP traffic, it can also pool backend connections, cache static resources, and use cookie-based session affinity.

Passthrough provides true end-to-end encryption without TLS termination at the proxy. It is the recommended mode when using X.509 client certificate authentication because the client certificate reaches {project_name} directly without being forwarded through an HTTP header, which eliminates the risk of forged client certificate headers. However, because the proxy cannot modify HTTP traffic, it cannot set forwarded headers or filter URL paths. This mode also requires {project_name} to present a certificate that covers all its externally visible hostnames, which can complicate certificate management.

Security considerations

When using re-encrypt:

  • Restrict network access so that {project_name} accepts connections only from the proxy. Without this restriction, clients could bypass the proxy and send forged forwarded headers directly to {project_name}.

    Set --proxy-trusted-addresses to the IP addresses of your proxy to ensure forwarded headers are accepted only from these IP addresses. Note that this is only weak protection because IP addresses can be spoofed.

  • Ensure the proxy overwrites (not just appends to) forwarded headers to prevent clients from injecting false values.

When using passthrough:

  • Restrict network access so that {project_name} accepts connections only from the proxy.

  • Forwarded headers are not applicable because the proxy cannot modify the encrypted traffic.

  • Enable the PROXY protocol (--proxy-protocol-enabled=true) to see the real client IP address. Note that the PROXY protocol is not compatible with the --proxy-headers option.

    The PROXY protocol header that the proxy adds to convey the client IP address is neither encrypted nor signed. Therefore, it can be spoofed, tampered with, or leaked. Mitigate this risk, for example, by restricting network access.

  • Allow a longer --shutdown-delay (for example, 10–30 seconds) to give keepalive connections time to drain during shutdown, since the proxy cannot signal a connection close at the HTTP connection level.

  • Due to HTTP/2 connection coalescing, do not configure {project_name} to be the proxy’s default backend when {project_name} is using a wildcard certificate and that certificate is shared among several services behind the proxy. In this configuration {project_name} will not be able to distinguish between misdirected requests and those sent by default - when no SNI information is sent or SNI does not match any backend.

Backchannel access considerations

If private backchannel access to {project_name} is requested, using a proxy might be challenging:

  • When the PROXY protocol is used for TLS passthrough, clients cannot connect directly to {project_name} without the PROXY protocol. Any direct connection would also allow spoofing of IP source addresses.

  • When using TLS re-encrypt, it might be impossible to restrict proxy traffic to specific source IP addresses when the proxy’s IP addresses are dynamic. Not restricting the proxy traffic would allow clients to spoof proxy headers.

In these setups, it might be better to stick with a single proxy and no backchannel access, or to deploy an internal proxy in addition to the external proxy.

When deploying a second proxy, there are additional considerations:

  • Restrict network access so that {project_name} accepts connections only from the two proxies, for example, by adding network filtering.

  • Both proxies need to use the same mode. Either both need to use TLS passthrough, or both need to use TLS re-encrypt.

  • If using TLS passthrough mode, both proxies can forward requests using the PROXY protocol. As {project_name} presents a single TLS certificate for both paths, it needs to include a subject alternative name (SAN) for the second path so callers can validate TLS on both paths. Note that this SAN will be visible on both paths. While this is not a technical problem, it still leaks an internal hostname on the public path.

  • If using TLS re-encrypt mode, both proxies would filter incoming HTTP headers, and add trusted HTTP headers.

Port to be proxied

{project_name} runs on the following ports by default:

  • 8443 (8080 when you enable HTTP explicitly by --http-enabled=true)

  • 9000

The port 8443 (or 8080 if HTTP is enabled) is used for the Admin UI, Account Console, SAML and OIDC endpoints, and the Admin REST API as described in the <@links.server id="hostname"/> {section}.

The port 9000 is used for management, which includes endpoints for health checks and metrics as described in the <@links.server id="management-interface"/> {section}.

You only need to proxy port 8443 (or 8080) even when you use different hostnames for frontend/backend and administration as described in <@links.server id="configuration-production"/>. You should not proxy port 9000 because health checks and metrics use that port directly, and you do not want to expose this information to external callers.

{project_name} with TLS passthrough

In TLS passthrough mode, the proxy forwards the raw TLS connection to {project_name} without terminating it. Because the proxy cannot inspect or modify HTTP traffic, it cannot set or overwrite HTTP headers such as Forwarded or X-Forwarded-*.

Warning

Do not set --proxy-headers when using TLS passthrough. The proxy cannot inject forwarded headers into the encrypted traffic, and any headers already present in the request originate from the client and cannot be trusted. Setting --proxy-headers in this mode would allow clients to spoof their IP address or protocol by sending forged headers directly to {project_name}.

Without forwarded headers, the only way for {project_name} to see the real client IP address is through the PROXY protocol, which is strongly recommended for TLS passthrough deployments.

PROXY Protocol

The PROXY protocol allows the proxy to prepend the original client connection information (such as the source IP address) to the TCP stream before forwarding it to {project_name}. This happens outside of the TLS-encrypted channel, so it works even though the proxy cannot modify HTTP traffic.

Enable it in {project_name} with the --proxy-protocol-enabled option:

<@kc.start parameters="--proxy-protocol-enabled true"/>

The proxy must also be configured to send the PROXY protocol header. Most proxies support both version 1 (text-based) and version 2 (binary). Consult your proxy’s documentation for the specific configuration directive (for example, send-proxy-v2 in HAProxy).

Note
The --proxy-protocol-enabled option and the --proxy-headers option are mutually exclusive. Setting both will result in a configuration error.

Blueprints

  • <@links.server id="haproxy-passthrough"/>

  • <@links.server id="traefik-passthrough"/>

{project_name} with TLS reencrypt

In TLS re-encrypt mode, the proxy terminates the client TLS connection and establishes a new TLS connection to {project_name}. Because the proxy can inspect and modify HTTP traffic, additional configuration options are available, such as forwarded headers, path filtering, sticky sessions, and client certificate forwarding.

Configure the reverse proxy headers

{project_name} parses the reverse proxy headers based on the proxy-headers option, which accepts several values:

  • forwarded enables parsing of the Forwarded header as per RFC 7239.

  • xforwarded enables parsing of non-standard X-Forwarded-* headers, such as X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port, and X-Forwarded-Prefix.

Note
If you are using a reverse proxy for anything other than TLS passthrough and do not set the proxy-headers option, then by default you will see 403 Forbidden responses to requests via the proxy that perform origin checking.

For example:

<@kc.start parameters="--proxy-headers forwarded"/>

Warning
If either forwarded or xforwarded is selected, make sure your reverse proxy properly sets and overwrites the Forwarded or X-Forwarded-* headers respectively. To set these headers, consult the documentation for your reverse proxy. Do not use forwarded or xforwarded with TLS passthrough. Misconfiguration will leave {project_name} exposed to security vulnerabilities.

Take extra precautions to ensure that the client address is properly set by your reverse proxy via the Forwarded or X-Forwarded-For headers. If these headers are incorrectly configured, rogue clients can inject false values and trick {project_name} into thinking the client is connecting from a different IP address than the actual one. This precaution is especially critical if you do any deny or allow listing of IP addresses.

Similarly, if your deployment uses fully dynamic URLs (--hostname-strict=false), rogue clients can forge the request’s host headers. This exposes {project_name} to Host header injection, allowing attackers to manipulate dynamically generated URLs.

Note
When using the xforwarded setting, the X-Forwarded-Port takes precedence over any port included in the X-Forwarded-Host.
Note
If the TLS connection is terminated at the reverse proxy (edge termination), enabling HTTP through the http-enabled setting is required.

Trusted Proxies

By default, {project_name} accepts proxy headers from any source. To restrict this, set the proxy-trusted-addresses option to a comma-separated list of addresses that identify your trusted reverse proxies. When configured, proxy headers from any other address are ignored.

The following address formats are supported:

Format Description Example

IPv4 address

A single IPv4 address.

192.168.0.32

IPv6 address

A single IPv6 address enclosed in square brackets.

[::1]

CIDR notation

An IP range in Classless Inter-Domain Routing notation. Use this to trust an entire subnet.

127.0.0.0/8 or ::1/128

Hostname

A DNS hostname such as localhost or proxy.example.com.

localhost

Address or hostname with port

An IP address or hostname with an optional port suffix. Use this when your proxy connects from a specific port.

192.168.0.32:8084 or [::1]:8084 or proxy.example.com:8084

Important
IPv6 addresses must be enclosed in square brackets, for example [::1] or [0:0:0:0:0:0:0:1]. If a bare IPv6 address without brackets is provided (for example ::1), {project_name} automatically converts it to the bracketed form and logs a warning. To avoid this warning, always use brackets or CIDR notation for IPv6 addresses.
Warning
Using hostnames is deprecated and will be removed in a future version. The behavior of hostnames is unreliable: they are resolved to IP addresses at startup and will not track changes to DNS records. If the hostname cannot be resolved at startup, a DNS lookup is performed on every request, which slows down request processing. Use IP addresses or CIDR notation instead.

For example, to trust a specific proxy IP and an entire subnet:

<@kc.start parameters="--proxy-headers forwarded --proxy-trusted-addresses=192.168.0.32,127.0.0.0/8"/>

To trust both IPv4 and IPv6 loopback addresses using CIDR notation:

<@kc.start parameters="--proxy-headers forwarded --proxy-trusted-addresses=127.0.0.1/32,::1/128"/>

Header filtering recommendations

When using TLS re-encrypt, the proxy can inspect and modify HTTP traffic. Use this capability to prevent external clients from injecting headers that affect identity resolution, access control, or observability.

Note
The following assumes that there is only a single proxy layer in front of {project_name}, and that it is not receiving traffic from another trusted proxy layer with trusted headers.

Configure the proxy to apply the following rules to incoming requests before forwarding them to {project_name}:

  • Overwrite Forwarded and X-Forwarded-* headers with the proxy’s own values rather than removing them, because {project_name} relies on these headers when --proxy-headers is configured.

  • Strip all other headers listed below entirely.

Depending on your security setup, you might want to allow some of the tracing headers through — either as-is or after sanitizing their values — for requests from trusted callers. This assessment depends on your trust boundaries and security trade-offs and is out of scope for this guide.

Header(s) Category Risk if not filtered

Forwarded, X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Prefix

Proxy identity

Clients can spoof their IP address, protocol, or host, affecting access control and audit logging. Forging the host allows Host header injection, enabling attackers to manipulate dynamically generated URLs. Overwrite these headers rather than stripping them.

X-Original-Forwarded-For

Proxy identity

Variant of X-Forwarded-For recognized by some proxies. Can be spoofed to bypass IP-based access controls.

X-Real-IP

Proxy identity

Trusted by some applications for rate limiting and audit logging. Can be spoofed to bypass IP-based restrictions.

X-Original-URL, X-Original-Method

Proxy identity

Used by authentication sub-request mechanisms in some proxies. Can be spoofed to manipulate path-based authorization decisions.

X-Forwarded-Access-Token

Proxy identity

Injected by some OAuth2 proxies. Can be spoofed to inject forged access tokens.

traceparent, tracestate

Distributed tracing

W3C Trace Context headers. External injection allows attackers to correlate requests in the tracing backend and map internal service dependencies.

baggage

Distributed tracing

W3C Baggage header. Can inject arbitrary key-value pairs into the trace context propagated to downstream services.

b3, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags

Distributed tracing

Zipkin B3 propagation headers. External injection inflates observability costs and enables cross-service correlation.

uber-trace-id

Distributed tracing

Jaeger propagation header (deprecated in favor of W3C Trace Context). Same risks as other tracing headers.

x-ot-span-context

Distributed tracing

OpenTracing propagation header (deprecated in favor of W3C Trace Context). Same risks as other tracing headers.

Exposed path recommendations

When using a reverse proxy, {project_name} only requires certain paths to be exposed. The following table shows the recommended paths to expose.

{project_name} Path Reverse Proxy Path Exposed Reason

/

-

No

When exposing all paths, admin paths are exposed unnecessarily.

/admin/

-

Only internally

Exposed admin paths lead to an unnecessary attack vector.

/realms/

/realms/

Yes

This path is needed to work correctly, for example, for OIDC endpoints.

/realms/master/

/realms/master/

Only internally

Assuming the administrative realm is called master, restrict /realms/master/ to internal access. See Isolating authentication for the administrative realm for more information.

/resources/

/resources/

Yes

This path is needed to serve assets correctly. It may be served from a CDN instead of the {project_name} path.

/.well-known/

/.well-known/

Yes

This path is needed to resolve Authorization Server Metadata and other information via RFC 8414.

/metrics

-

No

Exposed metrics lead to an unnecessary attack vector.

/health

-

No

Exposed health checks lead to an unnecessary attack vector.

/lb-check

/lb-check

Yes

Endpoint to be used for an external load balancer to check the availability of a {project_name} cluster.

We assume you run {project_name} on the root path / on your reverse proxy/gateway’s public API. If not, prefix the path with your desired one.

Note
If you configured an http-relative-path on the server, proceed as follows to use discovery with RFC 8414: Configure a reverse proxy to map the /.well-known/ path without the prefix to the path with the prefix on the server.

The lb-check endpoint is only available when the stateless or the multi-site feature is enabled. It is intended for external load balancers when you do not want to expose the management interface and its /health endpoints publicly. This is a non-blocking endpoint and should remain responsive under high load, so the load balancer does not remove a healthy cluster from rotation during transient overload.

Isolating authentication for the administrative realm

As a security best practice, restrict access to the authentication protocol endpoints of the administrative realm from public IP addresses. The following description assumes {project_name}'s default settings and that this realm is called master.

To do that, do not expose the path /realms/master/ to the public internet.

Depending on your topology, you can combine this with serving the Admin UI and Admin API on a dedicated hostname and port. See <@links.server id="hostname" anchor="administration-console-on-a-separate-hostname" /> for details.

Different context path on reverse proxy

By default, {project_name} is exposed through the root context path (/). If the proxy uses a different context path than {project_name}, one of the following must be done:

  • Use a simple hostname for the hostname option, xforwarded for the proxy-headers option, and have the proxy set the X-Forwarded-Prefix header.

  • Use a full URL for the hostname option including the proxy context path, for example using --hostname=https://my.keycloak.org/auth if {project_name} is exposed through the reverse proxy on /auth.

  • Change the context path of {project_name} itself to match the context path for the reverse proxy using the http-relative-path option.

For more details on exposing {project_name} on a different hostname or context path, including the Administration REST API and Console, see <@links.server id="hostname"/>.

Sticky sessions

In previous versions of {project_name}, sticky sessions could improve performance by routing all requests for a browser session to the same backend node, avoiding remote cache lookups for session data. {project_name} appended the owning node’s route to the AUTH_SESSION_ID cookie (for example, 123.node2) so that the load balancer could parse it for routing decisions.

Since the optimizations in 26.6, sticky sessions no longer bring a significant benefit given the complex setup on the proxy side to get this working. Authentication sessions are created on the node where the browser starts the session, and while a session may move to another node on a subsequent request, the overhead is low. Most interactions such as backchannel calls and token refreshes do not use cookies and are distributed across nodes regardless. The recommendation is to aim for an even distribution of load across {project_name} nodes instead of using sticky sessions.

The options spi-sticky-session-encoder--infinispan--should-attach-route and spi-sticky-session-encoder--remote--should-attach-route are deprecated since {project_name} 26.8 and will be removed in a future release. To suppress the deprecation warning at startup, set those SPI options to false:

<@kc.start parameters="--spi-sticky-session-encoder—​infinispan—​should-attach-route=false"/>

Enabling client certificate lookup

When the proxy is configured as a TLS termination proxy, the client certificate information can be forwarded to the server through specific HTTP request headers and then used to authenticate clients. You can configure how the server retrieves client certificate information depending on the proxy you are using.

Warning

Client certificate lookup via a proxy header for X.509 authentication is considered security-sensitive. If misconfigured, a forged client certificate header can be used for authentication. Extra precautions need to be taken to ensure that the client certificate information can be trusted when passed via a proxy header.

  • Double-check that your use case needs re-encrypt or edge TLS termination, which implies using a proxy header for client certificate lookup. TLS passthrough is recommended as a more secure option when X.509 authentication is desired because it does not require passing the certificate via a proxy header. Client certificate lookup from a proxy header is applicable only to re-encrypt and edge TLS termination.

  • If passthrough is not an option, implement the following security measures:

    • Configure your network so that {project_name} is isolated and can accept connections only from the proxy.

    • Make sure that the proxy overwrites the header that is configured in the spi-x509cert-lookup--<provider>--ssl-client-cert option.

    • Pay extra attention to the spi-x509cert-lookup--<provider>--trust-proxy-verification setting. Make sure you enable it only if you can trust your proxy to verify the client certificate. Setting spi-x509cert-lookup--<provider>--trust-proxy-verification=true without the proxy verifying the client certificate chain will expose {project_name} to a security vulnerability where a forged client certificate can be used for authentication.

The server supports some of the most common TLS termination proxies:

Provider Proxies

apache

Apache HTTP Server

haproxy

HAProxy

nginx

NGINX

traefik

Traefik (PassTLSClientCert middleware with pem: true)

rfc9440

Proxies compliant with RFC 9440

envoy

Envoy

To configure how client certificates are retrieved from the requests, you need to:

Enable the corresponding proxy provider

<@kc.build parameters="--spi-x509cert-lookup—​provider=<provider>"/>

Configure the HTTP headers

<@kc.start parameters="--spi-x509cert-lookup--<provider>--ssl-client-cert=SSL_CLIENT_CERT --spi-x509cert-lookup--<provider>--ssl-cert-chain-prefix=CERT_CHAIN --spi-x509cert-lookup--<provider>-certificate-chain-length=10"/>

When configuring the HTTP headers, you need to make sure the values you are using correspond to the names of the headers forwarded by the proxy with the client certificate information.

Common options for configuring providers are:

Option Description Supporting Providers

ssl-client-cert

The name of the header holding the client certificate

all but traefik and envoy, optional for rfc9440

ssl-cert-chain-prefix

The prefix of the headers holding additional certificates in the chain and used to retrieve individual certificates according to the length of the chain. For instance, a value CERT_CHAIN will tell the server to load additional certificates from headers CERT_CHAIN_0 to CERT_CHAIN_9 if certificate-chain-length is set to 10.

apache, nginx

ssl-cert-chain

The name of the header holding the full certificate chain as a single value. All chain certificates are contained in one header rather than split across indexed headers.

haproxy, rfc9440

certificate-chain-length

The maximum length of the certificate chain beyond the client certificate

all but envoy (defaults to 1)

Configuring the HAProxy provider

HAProxy can forward client certificates as base64-encoded DER using its ssl_c_der,base64 and ssl_c_chain_der,base64 sample fetches. The haproxy provider reads the client certificate from the ssl-client-cert header and the certificate chain from the ssl-cert-chain header.

The options and defaults specific to haproxy are as follows:

Option Description Default

ssl-client-cert

The name of the header holding the client certificate.

(none)

ssl-cert-chain

The name of the header holding the full certificate chain as a single base64-encoded DER value. HAProxy’s ssl_c_chain_der,base64 produces the concatenated DER encoding of all CA certificates in one value.

(none)

certificate-chain-length

The maximum number of certificates to load from the chain header.

1

Example HAProxy configuration:

http-request del-header Client-Cert
http-request del-header Client-Cert-Chain
http-request set-header Client-Cert %[ssl_c_der,base64] if { ssl_c_used } { ssl_c_verify 0 }
http-request set-header Client-Cert-Chain %[ssl_c_chain_der,base64] if { ssl_c_used } { ssl_c_verify 0 }

Corresponding {project_name} configuration:

<@kc.start parameters="--spi-x509cert-lookup—​provider=haproxy --spi-x509cert-lookup—​haproxy—​ssl-client-cert=Client-Cert --spi-x509cert-lookup—​haproxy—​ssl-cert-chain=Client-Cert-Chain --spi-x509cert-lookup—​haproxy—​certificate-chain-length=2"/>

If the number of intermediate certificates in your certificate chain is longer than the default, you must set the certificate-chain-length option to an appropriate value. Otherwise, the provider will discard the request.

Configuring the NGINX provider

The NGINX SSL/TLS module does not expose the client certificate chain. {project_name}'s NGINX certificate lookup provider rebuilds it by using the {project_name} truststore.

If you are using this provider, see <@links.server id="keycloak-truststore"/> for how to configure a {project_name} Truststore. The options and defaults specific to nginx are as follows:

Option Description Default

trust-proxy-verification

Enable trusting NGINX proxy certificate verification, instead of forwarding the certificate to {project_name} and verifying it in {project_name}.

false

cert-is-url-encoded

Whether the forwarded certificate is URL-encoded or not. In NGINX, this corresponds to the $ssl_client_cert and $ssl_client_escaped_cert variables.

true

Configuring the rfc9440 provider

If you stick to the header names mentioned in RFC 9440, you do not need to configure any additional options after selecting the rfc9440 provider. The options and defaults specific to rfc9440 are as follows:

Option Description Default

ssl-client-cert

The name of the header holding the client certificate

Client-Cert

ssl-cert-chain

The name of the header holding additional certificates in the chain. This is not a prefix but the full name of the header because RFC 9440 mandates that the chain certificates are contained in one header.

Client-Cert-Chain

If the number of intermediate certificates in your certificate chain is longer than the default, you must set the certificate-chain-length option to an appropriate value. Otherwise, the provider will discard the request.

Configuring the Traefik provider

The Traefik provider handles certificates forwarded by Traefik’s PassTLSClientCert middleware with pem: true. Traefik sends the client certificate and any intermediate CA certificates as PEM blocks in a single X-Forwarded-Tls-Client-Cert header, separated by commas. The traefik provider parses all certificates from this header.

Other than possibly changing the certificate-chain-length, you do not need to configure additional options for the traefik provider.

Configuring the Envoy provider

The Envoy provider automatically retrieves the client certificate and optional certificate chain from the x-forwarded-client-cert header. You do not need to configure additional options for the envoy provider.

Blueprints

  • <@links.server id="haproxy-reencrypt"/>

  • <@links.server id="traefik-reencrypt"/>

Graceful HTTP shutdown

When running {project_name} behind a reverse proxy or load balancer, graceful shutdown ensures that in-flight requests complete successfully during server termination, preventing connection errors for clients.

{project_name} enables graceful HTTP shutdown by default with configurable timeouts.

Understanding shutdown phases

The shutdown process consists of two phases:

Pre-shutdown delay

During this phase, {project_name} signals to load balancers and proxies that it is preparing to shut down. The server’s readiness endpoint returns a "not ready" status, allowing the load balancer to stop routing new requests to this instance. Existing TLS and HTTP keepalive connections are allowed to drain naturally. The server continues to process existing requests.

Shutdown timeout

After the pre-shutdown delay, {project_name} waits for in-flight HTTP requests to complete. Once the HTTP requests are complete, it uses the remaining time of the shutdown timeout to wait for caches to rebalance to avoid possible data loss. Once the timeout expires, the server shuts down regardless.

Default behavior

By default, {project_name} is configured with a 1-second pre-shutdown delay and a 10-second shutdown timeout. These defaults work well for most standard deployments where:

  • The load balancer reconfigures quickly (within 1 second)

  • Most requests complete within 1 second

  • The reverse proxy uses edge termination or re-encryption (not TLS passthrough)

Configuring shutdown timeouts

Advanced users can adjust the shutdown timeouts using CLI options based on their deployment characteristics.

bin/kc.sh start --shutdown-delay=<duration> --shutdown-timeout=<duration>

Available options:

--shutdown-delay

Length of the pre-shutdown phase during which the server prepares for shutdown. This period allows for load balancer reconfiguration and draining of TLS/HTTP keepalive connections. Default: 1s

--shutdown-timeout

The shutdown period waiting for currently running HTTP requests to finish. Default: 10s

Both values accept duration formats: 1s (seconds), 500ms (milliseconds), 2m (minutes), etc.

When to adjust shutdown timeouts

Consider adjusting these values based on your deployment configuration. The following table shows example scenarios:

Scenario Delay Timeout Reason

Load balancer polls readiness probe

16s

Assumptions:

  • A 5s poll interval and a load balancer reconfiguring after two successive failed probes.

  • 1s for reconfiguring the proxy.

  • Proxy using TLS re-encrypt or edge termination.

Calculation:

  • Allow three poll cycles for the load balancer to detect shutdown and the extra time for the load balancer to perform the reconfiguration:

    shutdown delay = 3 * 5s + 1s

TLS passthrough configuration

10‑30s

Longer delay allows keepalive connections to drain naturally and receive connection close signals.

Long-running admin API requests

10‑30s

Admin operations may take longer than typical user requests.

Test environments / quick restarts

0s

500ms

Minimize shutdown time when graceful draining is not needed.

Deployment orchestration reconfigures the proxy and drains connections before Pod termination

0s

No pre-shutdown delay needed if proxy is already reconfigured

Combined: TLS passthrough plus polled readiness

26‑56s

Delays add up: time for load balancer detection + connection draining

Example configurations

For production with TLS passthrough:

<@kc.start parameters="--shutdown-delay=30s" />

For load balancers that poll readiness:

<@kc.start parameters="--shutdown-delay=16s"/>

For test environments:

<@kc.start parameters="--shutdown-delay=0s --shutdown-timeout=500ms"/>

Note

The shutdown delay affects the minimum time required for a complete server restart. In Kubernetes environments, ensure your terminationGracePeriodSeconds is longer than the sum of shutdown-delay and shutdown-timeout to prevent forced termination.

</@tmpl.guide>