Depending on management cost and user experience requirements, it might be more sensible to configure internal service as https or as http.
I give example of 2 backend exposing NodePort on Kubernetes, just to keep proxy concern clearly separated.

SSL Termination at the Proxy (HTTP Mode)
Concept
- The proxy terminates TLS, handling encryption and certificate validation.
- Backends receive plain HTTP (or optionally HTTP/2) traffic.
- The proxy can inspect and modify headers (X-Forwarded-*) and perform routing, load balancing, caching, etc.
Browser <--HTTPS--> Proxy (SSL Termination) <--HTTP--> Backend
HAProxy Example (HTTP/2 Termination)
frontend https_frontend
bind *:443 ssl crt /etc/haproxy/ssl/wildcard_beerme.pem alpn h2,http/1.1
mode http
option httplog
# Match hostnames
acl host_redmine hdr(host) -i redmine.beerme
acl host_odoo hdr(host) -i odoo.beerme
# Route to backend
use_backend redmine_backend if host_redmine
use_backend odoo_backend if host_odoo
backend redmine_backend
mode http
balance roundrobin
option forwardfor
http-request set-header X-Forwarded-Proto https
http-request set-header X-Forwarded-Port 443
server r1 10.4.1.11:30080 check
server r2 10.4.1.12:30080 check
backend odoo_backend
mode http
balance roundrobin
option forwardfor
http-request set-header X-Forwarded-Proto https
http-request set-header X-Forwarded-Port 443
server o1 10.4.1.11:32036 check
server o2 10.4.1.12:32036 check
Notes:
- alpn h2,http/1.1 enables HTTP/2 between browser and proxy.
- Backend sees plain HTTP. Browser multiplexing happens between browser and proxy only.
- The backend receives headers indicating original HTTPS.
NGINX Example (HTTP/2 Termination)
server {
listen 443 ssl http2;
server_name redmine.beerme;
ssl_certificate /etc/nginx/ssl/wildcard_beerme.crt;
ssl_certificate_key /etc/nginx/ssl/wildcard_beerme.key;
location / {
proxy_pass http://redmine_backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
upstream redmine_backend {
server 10.4.1.11:30080;
server 10.4.1.12:30080;
}
Notes:
- http2 in listen enables browser-side HTTP/2.
- Backends only receive HTTP/1.1 requests unless proxy_http_version 2 is supported (NGINX default to HTTP/1.1 upstream).
- Headers inform backend of original HTTPS protocol.
SSL Passthrough (TCP/Stream Mode)
- Proxy does not terminate TLS; it simply forwards TCP based on SNI.
- Browser connects directly to the backend TLS session.
- End-to-end HTTP/2 multiplexing is preserved.
HAProxy TCP Passthrough Example
frontend ssl_passthrough
bind *:443
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
acl redmine_sni req.ssl_sni -i redmine.beerme
acl odoo_sni req.ssl_sni -i odoo.beerme
use_backend redmine_tcp_backend if redmine_sni
use_backend odoo_tcp_backend if odoo_sni
backend redmine_tcp_backend
mode tcp
server r1 10.4.1.11:30080 check
server r2 10.4.1.12:30080 check
backend odoo_tcp_backend
mode tcp
server o1 10.4.1.11:32036 check
server o2 10.4.1.12:32036 check
NGINX Stream Passthrough Example
stream {
map $ssl_preread_server_name $backend_name {
redmine.beerme redmine_backend;
odoo.beerme odoo_backend;
default blackhole;
}
upstream redmine_backend {
server 10.4.1.11:30080;
server 10.4.1.12:30080;
}
upstream odoo_backend {
server 10.4.1.11:32036;
server 10.4.1.12:32036;
}
server {
listen 443;
proxy_pass $backend_name;
ssl_preread on;
}
}
Notes:
- Backend certificates must match domain names (redmine.beerme, odoo.beerme) or use a wildcard certificate.
- Browser-side HTTP/2 multiplexing is fully preserved.
- Proxy cannot inspect HTTP headers.
Browser Performance
| Mode | Browser HTTP/2 Multiplexing | Backend Certificate Needed | Pros | Cons |
|---|---|---|---|---|
| HTTP SSL Termination | Only browser↔proxy | Proxy cert only | Header manipulation, routing, caching | Multiplexing not end-to-end |
| SSL Passthrough | Browser↔backend (full) | Backend cert valid | Full HTTP/2 performance | Backend must handle TLS, no header inspection |
Key takeaway:
- For maximum page serving speed and real end-to-end HTTP/2, passthrough is superior.
- SSL termination simplifies backend management and centralizes certs, but may limit multiplexing performance.
SSL Termination in Proxy, but talking with HTTP/2 backend (Stateless REST)
To take advance from stateless nature of REST requests, the better option is to let the proxy reuse the http/2 backend connection for more clients. This also reduce the total number of connection kept by the backend server, and the total number of TLS handshake required:
- proxy keep one connection per backend
- proxy negotiate one TLS per backend
- proxy reuse the same backend for requests arriving from different clients
For making nginx talk http/2 to the backend:
server {
listen 443 ssl http2;
server_name redmine.beerme;
ssl_certificate /etc/nginx/ssl/self.crt;
ssl_certificate_key /etc/nginx/ssl/self.key;
location / {
proxy_pass http://backend_redmine;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Enable HTTP/2 to backend
proxy_http_version 2;
proxy_set_header Connection "";
}
}