Routes or Ingress on OpenShift, and the three TLS modes
Edge, passthrough and re-encrypt termination compared, what each does to client certificates and HTTP headers, and when an Ingress object is the better choice.
The three termination modes
- Edge
- Router decrypts, forwards HTTP
- Passthrough
- Router forwards TLS untouched
- Re-encrypt
- Router decrypts, re-encrypts to pod
- Cert lives
- Route / Pod / Both
- mTLS to client
- Only passthrough
OpenShift gives you two ways to expose an HTTP service and most teams pick one by accident. The choice matters less than the TLS mode underneath it, which is where the real behaviour differences are.
Ingress works, and becomes a Route
Create an Ingress on OpenShift and the ingress-to-route controller creates a matching Route for you. Portable manifests keep working, and oc get route shows objects you never wrote.
Use Ingress when the manifest has to run unmodified on more than one distribution. Use a Route when you need something Ingress cannot express, which in practice means passthrough or re-encrypt TLS, or router-specific timeout and balancing behaviour.
There is no performance difference. Both end up as configuration in the same HAProxy router pods.
Edge: the default, and usually right
The router holds the certificate, terminates TLS, and forwards plain HTTP to your pod over the cluster network.
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: web
spec:
host: app.example.com
to:
kind: Service
name: web
port:
targetPort: http
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
Your application speaks HTTP and knows nothing about certificates. Certificate rotation happens in one place. insecureEdgeTerminationPolicy: Redirect sends port 80 to 443 rather than serving both.
The consequence to be aware of: traffic between the router and your pod is unencrypted. On most clusters that is a private SDN and the threat model accepts it. In an environment that requires encryption in transit end to end, it does not.
Your application also stops seeing the real client address on the socket. The router sets X-Forwarded-For, X-Forwarded-Proto and Forwarded; frameworks generally need telling to trust them, and a login flow that builds absolute URLs from the request will emit http:// links until you do.
Passthrough: when the pod must own the certificate
The router does not decrypt at all. It reads the SNI header to pick a backend and forwards the TLS stream untouched.
tls:
termination: passthrough
Pick this when:
- The application requires mutual TLS and must see the client certificate. Nothing else preserves it.
- A compliance rule requires that no intermediary can read the payload.
- The workload is not HTTP — a database or message broker over TLS.
The costs are real. The router cannot read paths, so path-based routing is gone and one hostname maps to one service. It cannot insert X-Forwarded-For, so your application sees the router’s address as the client. Certificates now live in a Secret mounted into your pods, and rotating them is a rollout rather than a Route edit.
Re-encrypt: encrypted end to end, still routable
The router terminates the client’s TLS with the public certificate, then opens a new TLS connection to the pod using a separate certificate.
tls:
termination: reencrypt
destinationCACertificate: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
You get encryption on both hops, plus path routing and forwarded headers, because the router can read the request. This is the mode to reach for when a security review rejects edge but you still need HTTP-aware routing.
The backend certificate does not need to be public. OpenShift’s service serving certificate feature will issue one for you: annotate the Service and it gets an internal certificate signed by a CA the cluster already trusts.
metadata:
annotations:
service.beta.openshift.io/serving-cert-secret-name: web-internal-tls
Then reference that CA in destinationCACertificate and let the platform rotate it.
Choosing, in one pass
| Edge | Passthrough | Re-encrypt | |
|---|---|---|---|
| Router can read the request path | Yes | No | Yes |
| Path-based routing on one hostname | Yes | No | Yes |
| X-Forwarded-For carries the real client | Yes | No | Yes |
| Client certificate reaches the pod | No | Yes | No |
| Encrypted on the router-to-pod hop | No | Yes | Yes |
| Certificate rotates on the Route alone | Yes | No | Partial |
| Pod must hold its own certificate | No | Yes | Yes |
| Works for non-HTTP protocols | No | Yes | No |
Re-encrypt rotates the public certificate on the Route but still needs a backend certificate in the pod — which the service serving certificate feature can issue and rotate for you.
The objective people skip
Exposing a plain TCP service — a database, a broker, something that is not HTTP and not TLS either — is not a Route problem at all. Routes are HTTP and TLS constructs. You need a LoadBalancer Service, or NodePort, or an ingress controller configured for TCP.
That gap is exactly the EX280 objective named “expose non-HTTP/SNI applications”, and it is the one candidates most often meet for the first time under exam conditions. It is also the one that surprises teams in production, usually the week they add a message queue.
A note on wildcard routes
Wildcard routes exist and are disabled on the default router for good reason: one team claiming *.example.com can capture traffic intended for another. If you need them, enable them on a dedicated router shard with a restricted namespace selector rather than on the default one.
Next steps
Practise it
Run the DO280 track in a real terminal
Every objective on CertLabs is graded against live system state rather than the command you typed, on a sandboxed cluster that resets between exercises. The DO280 track covers Deployments, scaling, GitOps.
Open CertLabsCertLabs is our own practice platform.
Get help
Running this in production?
We operate Kubernetes and OpenShift for clients across the EU and the Gulf, and train the teams who inherit them. Platform assessments, migrations and hands-on enablement.
Talk to us