Skip to main content

Pass TLS through at Layer 4

Inspect 1.36

TLS passthrough terminates the TLS connection inside your pod instead of at the ingress controller. The load balancer and the cluster move the raw encrypted bytes and never decrypt them, so your application performs the TLS handshake itself. Use this when the pod must hold the private key, when you need mutual TLS all the way to the application, or when the protocol is not HTTP.

This works because the load balancer runs at Layer 4. A Layer 7 (HTTP) load balancer or ingress reads the request and can route by hostname or path, which means it must decrypt TLS first. A Layer 4 (TCP) load balancer moves bytes and never inspects them, so the encrypted connection reaches your pod untouched.

When to terminate in the app instead of the edge#

is the common choice: the controller holds one certificate and every application behind it speaks plain HTTP. Pass through instead when:

  • the application must see the client certificate for mutual TLS,
  • a compliance rule says the key never leaves the workload,
  • or the protocol is raw TCP or something other than HTTP, so an HTTP ingress cannot route it.

The simplest passthrough: a TCP Service#

For a single application, a type: LoadBalancer Service on the TLS port moves the connection directly to the pod, and the pod terminates TLS:

service.yamlyaml
		apiVersion: v1
kind: Service
metadata:
  name: my-app-tls
  annotations:
    load-balancer.hetzner.cloud/location: fsn1
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - name: https
      port: 443
      targetPort: 8443
	

The pod listens on 8443 with its own certificate and performs the handshake. Nothing in the path decrypts the traffic.

Route several hosts by SNI#

One TCP Service sends everything on the port to a single application. To place several TLS applications behind one load balancer and still terminate in each pod, you need a component that reads the SNI (Server Name Indication, the hostname the client sends in the clear at the start of the TLS handshake) and forwards it without decrypting.

Enable the controller flag --enable-ssl-passthrough, then annotate an Ingress with nginx.ingress.kubernetes.io/ssl-passthrough: "true". The controller reads the SNI and forwards the still-encrypted connection to the backend Service. This carries a cost: passthrough routing runs outside the controller's normal path and adds latency, so use it only for the hosts that need it.

Keep the client address#

At Layer 4 the load balancer still replaces the source address. To provide the pod with the real client IP alongside the passed-through TLS, enable and have the application read the PROXY header before the TLS bytes. For the full picture of what the pod sees and the trade-offs, read .

The trade-off#

Passthrough moves certificate handling, renewal, and TLS tuning into every application that uses it, and you lose the edge features an ingress provides: a single place for certificates, HTTP routing, and header rewriting. Terminate at the edge unless a genuine requirement (mutual TLS to the application, key custody, or a non-HTTP protocol) forces the key into the pod.