skip to content

TLS Termination, Passthrough & mTLS

You learn the three ways TLS meets a proxy — terminate at the edge, pass the connection through untouched, or terminate and re-encrypt to the backend — and what each means for where private keys live, what routing decisions are still possible, and what the backend can know about the client. Interviewers ask because it is the one architectural question that touches security, routing and compliance at the same time, and mTLS between hops is the natural follow-up.

on this pageshow

questions

5

A load balancer in front of your web servers is configured to "terminate TLS". Which parts of the path from the browser to the application are encrypted after that, and which are not?

level: juniorimportance: must knowfreq 72%

answer

  1. one connection ends, another begins
  2. who holds the private key
  3. the backend hop is a separate decision
  4. app judges the scheme from its own socket
  5. redirect loop, http:// links, dropped Secure cookies

basics

~20 s

Terminating TLS means the load balancer holds the certificate's private key and completes the HTTPS handshake itself, so the browser-to-load-balancer hop is encrypted and the load-balancer-to-application hop is plain HTTP unless you separately encrypt it.

solid answer

~40 s

TLS protects one connection between two endpoints, and the endpoint that owns the private key and finishes the handshake is the one that terminates it. When the load balancer terminates, the browser's encrypted connection ends there: the balancer decrypts the request, and what reaches the application is a second, separate connection that is plain HTTP by default. That is exactly why the balancer can now route on path or Host, add headers, compress, or cache — it can read the request. The cost is that the application no longer knows on its own that the client used HTTPS, and its socket peer is the balancer, not the client, so it sees the balancer's IP. Whether the second hop stays plaintext or gets re-encrypted is a separate decision, not something termination settles for you.

code

bash · 2 lines
bash
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer
curl -sS -o /dev/null -w '%{http_code}\n' http://app-server.internal:8080/health

go deeper

for a junior

Be able to say plainly that the encrypted connection ends at the load balancer and a second, separate connection continues to the app, and that the certificate and its private key live wherever the handshake is completed.

for a middle

Explain the mechanics that follow: the proxy can read and rewrite HTTP only because it decrypted, the backend judges the scheme from its own plaintext socket, and forwarded headers are how the lost facts are restored.

for a senior

Show you have debugged the consequences in production — the redirect loop, the dropped Secure cookie, per-IP limits that count only proxy addresses — and that you know a forwarded header is only safe when the trusted-proxy boundary is configured.

for a principal

Frame the edge as a deliberate decryption point: one tier that can read every request and impersonate the site. Be ready to argue where key custody belongs and who is allowed shell access there.

## What "terminating" means TLS secures a *connection* between two endpoints. One of those endpoints proves who it is by presenting a certificate and demonstrating that it holds the matching private key; from then on both ends share symmetric keys and everything above the record layer — the HTTP request line, the headers, the body — is ciphertext to anyone in between. "Terminating TLS" simply names which box is that endpoint. If the load balancer holds the private key for `www.example.com` and completes the handshake, the client's TLS session ends at the load balancer. Nothing about the client's request travels further in encrypted form unless the load balancer deliberately encrypts it again on a *new* connection. ## The two hops Once a proxy terminates, there are always at least two independent connections, and it is worth drawing them: ``` browser --- TLS (encrypted) ---> load balancer --- HTTP (plaintext) ---> app server hop 1: client's session hop 2: a separate connection key + cert live here ^ opened by the balancer ``` Hop 1 and hop 2 are unrelated protocols-wise. Hop 1 can be TLS 1.3 with HTTP/2; hop 2 can be cleartext HTTP/1.1. The balancer is a full participant in both: a server on hop 1, a client on hop 2. ## What the proxy gains Because it decrypts, the proxy can read HTTP. That unlocks essentially everything people buy a reverse proxy for: routing by path, Host or header; sticky sessions from a cookie; inserting the forwarded headers that tell the backend who the client was; response compression; caching; a web application firewall; per-request retries to a different backend; and translating HTTP/2 or HTTP/3 on the client side into HTTP/1.1 on the backend side. None of that is possible against ciphertext. ## What the application loses The application's view narrows to hop 2, and two facts disappear from it: - **The client's IP address.** The TCP peer of the application is the load balancer, so the remote address it reads is the balancer's. Every client looks like it comes from a handful of proxy addresses, which quietly breaks per-IP rate limiting, geo logic and audit logs. - **The fact that the request was HTTPS.** Frameworks derive "is this request secure?" from their own socket. On hop 2 that socket is plaintext, so the framework says "no". That second one produces the single most common symptom in this whole area. The application is configured to redirect insecure requests to `https://`. A browser asks for `https://www.example.com/`, the balancer decrypts it and forwards plain HTTP, the app decides the request is insecure and answers a redirect to `https://www.example.com/`, and the browser comes back over HTTPS again — forever. The same cause makes the app emit `http://` absolute URLs in password-reset links and refuse to set cookies marked `Secure`. The fix is not to stop terminating; it is to tell the application what the proxy knows. The proxy states the original scheme and client address in forwarded request headers, and the application is configured to trust those headers *only* when the request arrived from the proxy — otherwise any client can claim to be anyone from anywhere. ## Where the key lives, and why that matters Terminating at the edge concentrates the private key in one tier. That is usually an advantage: renewal and rotation happen in one place, the key can sit in dedicated hardware or a managed key service, and the set of machines that can impersonate your site is small and auditable. The flip side is equally real — that tier is a decryption point. Anyone with shell access or a core dump on the balancer sees plaintext requests, including whatever secrets are in the headers. Whether that is acceptable is a design question, not a default. ## How to check what is actually happening From outside, open a TLS connection and look at whose certificate comes back and what protocol was negotiated — if the certificate is the load balancer's wildcard rather than the origin's, something is terminating in front of you. From inside, request the app directly on its own port: if it answers plain HTTP on 8080, hop 2 is unencrypted. And log the forwarded scheme header alongside the framework's own notion of the scheme; when they disagree, you have found your redirect loop.

  • If the load balancer terminates TLS, how can the application still find out that the client used HTTPS?
    The proxy states it in a forwarded request header describing the original scheme, and the application is configured to trust that header only for connections arriving from the proxy's addresses. Trusting it unconditionally is worse than not having it: any client could then assert that its plaintext request was secure and slip past a scheme check.
  • Does terminating at the load balancer mean the backend hop must be plaintext?
    No. Terminating only settles where the client's session ends. The proxy may open a fresh TLS connection to the backend — commonly called re-encryption or bridging — while still reading and routing the request in between. Plaintext on the second hop is a common default, not a consequence.
  • Why does the application see the same handful of source addresses for every request once a proxy terminates in front of it?
    Because the application's TCP peer is the proxy. The client's connection stopped at the proxy, and the proxy opened its own connection from its own address, usually reusing a small pool of them. The client's real address survives only if the proxy passes it on explicitly.

saying these in an interview costs you the question

  • Thinks the client's TLS session continues to the backend
  • Believes the backend hop is automatically encrypted too
  • Says the app can read the client IP from the socket regardless
  • Assumes HTTPS in the browser proves end-to-end encryption
  • Blames an expired certificate for an https redirect loop

context

open as a page

You are placing a proxy in front of an HTTPS service and must choose between terminating TLS at the proxy, passing the TLS connection through untouched, and terminating then re-encrypting to the backend. What does each choice buy you, and where does the private key live in each?

level: middleimportance: must knowfreq 64%

basics

~20 s

Termination puts the key on the proxy and lets it read and route HTTP; passthrough leaves the key on every backend and reduces the proxy to forwarding bytes; re-encryption terminates, routes, then opens a second TLS connection to the backend, so both hops are encrypted at the cost of two handshakes and two certificate sets to manage.

open as a page

An edge proxy authenticates callers with mutual TLS, and the application behind it needs to know which client certificate was presented. How should that identity reach the application, and what is the classic way this arrangement is defeated?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The proxy verifies the client certificate and then asserts the resulting identity to the application in a request header, because the certificate itself cannot survive termination. That assertion is defeated whenever the header is not stripped from inbound requests or the application is reachable without going through the proxy.

open as a page

A compliance requirement states that traffic must be encrypted in transit. Your platform terminates TLS at the edge and speaks plain HTTP internally. How would you decide between leaving it as is, re-encrypting to every backend, and passing TLS through to the origin?

level: principalimportance: should knowfreq 33%

basics

~20 s

Start from what the control actually says and which network segments it covers, then pick the cheapest topology that satisfies it: plaintext inside a genuinely bounded segment, re-encryption where wires leave a trust boundary, and passthrough only when the edge must never hold the key or see plaintext.

open as a page

A layer-4 proxy routes incoming HTTPS connections to different backends without ever decrypting them. What information in the TLS ClientHello makes that possible, and which routing decisions remain off the table?

level: middleimportance: nice to knowfreq 40%

basics

~20 s

The ClientHello is sent before encryption begins, so a proxy can read its Server Name Indication and ALPN list — the hostname the client asked for and the protocols it offers — and pick a backend from those. Anything inside the encrypted session, such as the URL path, headers or cookies, stays invisible.

open as a page