Kubernetes Gateway API: TLSRoute support

Last published : Nov 02, 2026
The NetScaler Kubernetes Gateway Controller supports the TLSRoute resource from the Kubernetes Gateway API. TLSRoute routes TLS traffic at a Gateway listener, in two modes:
Mode What NetScaler does Backend receives
Passthrough Forwards the raw TLS bytes without decrypting, selecting the backend from the SNI host name in the ClientHello (an SSL bridge). Encrypted TLS — the backend terminates TLS.
Terminate (default) Terminates TLS with the Gateway certificate and routes by the HTTP Host header after decryption, forwarding plaintext HTTP to the backend. Plaintext HTTP — NetScaler presents the certificate.
TLSRoute is served at gateway.networking.k8s.io/v1 (Gateway API v1.5.0 and later) and is supported on NetScaler 14.1 and later.

Prerequisites

  • The Gateway API standard-channel CRDs are installed (TLSRoute is served as v1):
    kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.0/standard-install.yaml
  • The NetScaler Kubernetes Gateway Controller is deployed, and a GatewayClass references its controller name:
    apiVersion: gateway.networking.k8s.io/v1
    kind: GatewayClass
    metadata:
      name: citrix-gateway-class
    spec:
      controllerName: citrix.com/nsgw-controller
  • For Terminate mode, a Kubernetes TLS Secret that holds the Gateway certificate:
    kubectl create secret tls gateway-tls-secret --cert=tls.crt --key=tls.key -n default

TLS passthrough

In Passthrough mode, the Gateway routes the raw TLS bytes to the backend based on the SNI host name in the ClientHello. NetScaler never decrypts the traffic, and the backend presents its own certificate. The controller programs an SSL policy on the listener's SSL virtual server that selects the backend by SNI, and the backend is reached through a TCP service group.
Use passthrough for backends that manage their own TLS (for example, mutual TLS or custom certificate requirements) or for legacy TLS applications.
# Gateway — TLS listener in Passthrough mode (no certificateRefs needed)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: passthrough-gw
  namespace: default
spec:
  gatewayClassName: citrix-gateway-class
  addresses:
    - type: IPAddress
      value: <VIP>       # NetScaler VIP
  listeners:
  - name: tls-passthrough
    protocol: TLS
    port: 443
    tls:
      mode: Passthrough
    allowedRoutes:
      namespaces:
        from: Same
      kinds:
      - kind: TLSRoute
---
# TLSRoute — exact host name
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
  name: passthrough-route
  namespace: default
spec:
  parentRefs:
  - name: passthrough-gw
    sectionName: tls-passthrough
  hostnames:
  - "app.example.com"
  rules:
  - backendRefs:
    - name: tls-backend-svc     # Service whose pods serve TLS on port 443
      port: 443
---
# TLSRoute — wildcard host name (matches foo.example.com and foo.bar.example.com, not example.com)
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
  name: passthrough-wildcard
  namespace: default
spec:
  parentRefs:
  - name: passthrough-gw
    sectionName: tls-passthrough
  hostnames:
  - "*.example.com"
  rules:
  - backendRefs:
    - name: tls-backend-svc
      port: 443

TLS terminate

In Terminate mode, NetScaler presents the Gateway certificate to the client, terminates TLS, and forwards plaintext HTTP to the backend. After decryption the controller routes by the HTTP Host header through a content switching virtual server, and the backend is reached through an HTTP service group.
Use terminate for standard HTTPS where the backend speaks plain HTTP, for centralized certificate management at the Gateway, or for applications that cannot manage their own TLS.
# TLS Secret — Gateway certificate
apiVersion: v1
kind: Secret
metadata:
  name: gateway-tls-secret
  namespace: default
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-cert>
  tls.key: <base64-encoded-key>
---
# Gateway — TLS listener in Terminate mode
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: terminate-gw
  namespace: default
spec:
  gatewayClassName: citrix-gateway-class
  addresses:
    - type: IPAddress
      value: <VIP>       # NetScaler VIP
  listeners:
  - name: tls-terminate
    protocol: TLS
    port: 443
    tls:
      mode: Terminate           # default when omitted
      certificateRefs:
      - kind: Secret
        name: gateway-tls-secret
    allowedRoutes:
      namespaces:
        from: Same
      kinds:
      - kind: TLSRoute
---
# TLSRoute — exact host name, plain HTTP backend
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
  name: terminate-route
  namespace: default
spec:
  parentRefs:
  - name: terminate-gw
    sectionName: tls-terminate
  hostnames:
  - "secure.example.com"
  rules:
  - backendRefs:
    - name: http-backend-svc    # Service whose pods serve plain HTTP on port 80
      port: 80
---
# TLSRoute — wildcard host name (matches foo.example.com and foo.bar.example.com, not example.com)
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
  name: wildcard-terminate-route
  namespace: default
spec:
  parentRefs:
  - name: terminate-gw
    sectionName: tls-terminate
  hostnames:
  - "*.example.com"
  rules:
  - backendRefs:
    - name: http-backend-svc
      port: 80
---
# TLSRoute — no host name (catch-all, lowest priority)
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
  name: catchall-terminate-route
  namespace: default
spec:
  parentRefs:
  - name: terminate-gw
    sectionName: tls-terminate
  rules:
  - backendRefs:
    - name: default-backend-svc
      port: 80

Host name matching

TLSRoute hostnames Matches Does not match
app.example.com app.example.com (case-insensitive) api.example.com
*.example.com foo.example.com, bar.example.com, foo.bar.example.com (the wildcard matches subdomains at any depth) example.com
(no host names) all traffic (catch-all, lowest priority) —
When more than one route or host name can match a connection, NetScaler evaluates them in this order (highest to lowest priority):
  1. Exact host name (for example, app.example.com)
  2. Wildcard host name (for example, *.example.com)
  3. No host name (catch-all)
Note:
A TLSRoute rule can list more than one backendRef. NetScaler sends the traffic in round-robin order across the pods of all the listed backends, so a backend with more pods receives a larger share. The Gateway API weight field on backendRefs is not applied.