SSL Behind a Reverse Proxy: Catch TLS Faults with One OpenSSL Check
By Nick Phillips, Founder
SSL Behind a Reverse Proxy: Catch TLS Faults with One OpenSSL Check

For most setups behind a reverse proxy, terminate TLS at the proxy so you can route by hostname, inspect headers, and manage certificates in one place. Before you touch config, run openssl s_client -connect yourhost:443 -servername yourhost and check whether your backend actually sees X-Forwarded-Proto: https. That single check tells you most of what’s broken.
TL;DR:
- Use passthrough only when the backend must retain the private key; choose bridging when internal traffic also needs encryption, accepting separate certificates.
- After TLS termination, configure the application to trust the X-Forwarded-Proto header; otherwise, frameworks may generate HTTP links and trigger redirects or mixed content.
- For TLS connections to a backend, enable SNI and verify its certificate hostname against the exact backend hostname, since mismatches commonly cause 502 errors.
- Check access logs for the header’s value, then test with openssl and curl; presence alone does not prove the proxy sets the scheme correctly.
Table of Contents
- 1. When to use termination, passthrough, or bridging
- 2. How proxies communicate the original scheme: X-Forwarded-Proto and Forwarded
- 3. Actionable proxy configs: nginx, Apache, HAProxy (key directives)
- 4. Configuring the backend: trusting headers, re-encrypting, and hostname verification
- 5. Troubleshooting checklist: TLS handshake failures, SNI mismatches, and mixed content
- 6. Verification checklist and quick tests to run now
- 7. Author perspective and pragmatic defaults
- Otterwatch: monitoring to complement configuration
- FAQ
- Sources
1. When to use termination, passthrough, or bridging
Termination means the proxy decrypts TLS, talks plain HTTP to the backend, and handles all the Layer 7 work: routing by path, rewriting headers, load balancing by content. This is the right default when you need any of that, and it’s what most nginx or HAProxy setups do.
Passthrough means the proxy never decrypts anything. It forwards the encrypted TLS stream straight to the backend based on the SNI hostname. You lose L7 features entirely, but the backend holds the private key and nothing in between ever sees plaintext.
Bridging decrypts at the proxy, then re-encrypts a fresh TLS connection to the backend. You get both L7 visibility and encrypted transport to the backend, at the cost of running two certificates instead of one.
- Terminate when you need routing, header rewriting, or centralized certificate management.
- Pass through when compliance or architecture requires the backend to hold the only private key.
- Bridge when you want L7 control and encrypted backend traffic, common in zero-trust internal networks.
2. How proxies communicate the original scheme: X-Forwarded-Proto and Forwarded
Once you terminate TLS at the proxy, the backend talks to it over plain HTTP, and the backend has no way to know the original request was HTTPS unless the proxy tells it. That’s the whole job of X-Forwarded-Proto, a defect standard header that carries the scheme the client actually used to connect.
The Forwarded header is the standardized alternative, bundling proto, for, and host into one field instead of three separate X-Forwarded-* headers. It’s cleaner, but it also exposes more client-identifying information when multiple proxies append to it, so weigh that before switching.
X-Forwarded-Proto: httpstells the backend to generate HTTPS links, not HTTP ones.Forwarded: proto=https;host=example.com;for=203.0.113.5packs the same information into one standardized header.- Check your access logs for the header value, not just its presence. A proxy that forwards the header but never sets it correctly is a common silent failure.
Pro Tip: If you’re migrating from X-Forwarded-Proto to Forwarded, support both during the transition. Some middleware only reads one or the other.
3. Actionable proxy configs: nginx, Apache, HAProxy (key directives)
For nginx, the three directives that fix almost every forwarding and backend-TLS problem are:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_ssl_server_name on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;
proxy_set_header forwards the scheme. proxy_ssl_server_name on sends SNI when nginx connects to a backend over TLS, which matters if that backend hosts multiple certificates. The nginx proxy module documentation lists the full set of proxy_ssl_* options, including protocol and cipher controls.
For Apache, pair mod_proxy with mod_ssl and set the header explicitly:
RequestHeader set X-Forwarded-Proto "https"
SSLProxyEngine on
SSLProxyCheckPeerName on
SSLProxyCheckPeerName verifies the backend’s certificate hostname matches what Apache expects, which catches a lot of silent misconfigurations.
For HAProxy, the mode matters more than any single directive. In http mode, HAProxy terminates TLS and can set X-Forwarded-Proto itself. In tcp mode, it passes the encrypted stream through untouched based on SNI, which is how you’d configure passthrough rather than termination.
- nginx: set
proxy_ssl_server_name onwhenever the backend uses SNI-based routing. - Apache: enable
SSLProxyCheckPeerNameto catch hostname mismatches early. - HAProxy: use
tcpmode for passthrough,httpmode for termination with header rewriting.
Pro Tip: Test directive changes with a single curl request before rolling them out broadly. A typo in proxy_set_header fails silently and just omits the header.
4. Configuring the backend: trusting headers, re-encrypting, and hostname verification
Setting the header at the proxy is half the job. The backend application also has to trust it, or it’ll keep generating HTTP links and triggering mixed-content warnings.

In Django, set SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https'). In Express, call app.set('trust proxy', 1). WordPress needs a snippet in wp-config.php checking $_SERVER['HTTP_X_FORWARDED_PROTO'] and forcing $_SERVER['HTTPS'] = 'on' when it equals https, per the behavior X-Forwarded-Proto documentation describes, since failing to configure this is a frequent cause of mixed content and broken absolute URLs.
If you’re bridging rather than terminating flat, re-encrypt to the backend only when you genuinely need encrypted transport internally, and make sure the backend certificate’s SANs match the hostname the proxy connects with.
- Django, Express, and most frameworks need an explicit “trust this header” setting, it’s rarely on by default.
- WordPress requires a manual check against
HTTP_X_FORWARDED_PROTOsince it has no built-in proxy awareness. - Match backend certificate SANs to the exact hostname used in the proxy-to-backend connection, not the public one.
5. Troubleshooting checklist: TLS handshake failures, SNI mismatches, and mixed content
When something breaks, work through this in order instead of guessing:
- Reproduce the failure with a plain
curl -v https://yourhostand read the exact error. - Check the proxy’s error log for handshake or certificate verification messages.
- Run
openssl s_client -connect backendhost:443 -servername backendhostto confirm the backend presents the certificate you expect. - Compare the certificate’s SANs against the hostname the proxy actually connects to, mismatches here are a common cause of 502 errors.
- If the handshake fails outright, check protocol and cipher support on both ends, outdated TLS versions on either side will reject the connection.
A missing proxy_ssl_server_name on directive is one of the most common causes of a 502 when proxying TLS to a backend that uses SNI. For WordPress specifically, mixed content after termination almost always traces back to the HTTP_X_FORWARDED_PROTO check missing from wp-config.php.
Pro Tip: If you manage WordPress sites behind a proxy, our partners at Tcosi cover redirect and monitoring setups that catch these mixed-content issues before they hit search rankings.
6. Verification checklist and quick tests to run now
Once config changes are in, confirm them with a short sequence rather than trusting the dashboard:
- Run
openssl s_client -connect yourhost:443 -servername yourhostand inspect the returned certificate chain and SNI response. - Run
curl -v https://yourhostand look for the certificate details in the verbose output. - Simulate a proxied request with
curl --header 'X-Forwarded-Proto: https' http://backendhostto confirm the backend respects the header. - Load the site in a browser and check the console for mixed-content warnings.
- Check your access logs to confirm
X-Forwarded-Protois present and set correctly on live traffic, not just in your test request.
If every step checks out, you’ve confirmed the proxy forwards the right signals and the backend trusts them, which is the whole point of this setup.
7. Author perspective and pragmatic defaults

The mistake I see most often isn’t choosing the wrong TLS mode, it’s forgetting to tell the backend what mode you chose. Teams terminate TLS at the proxy, skip X-Forwarded-Proto, and spend an afternoon debugging redirect loops that a single header would have prevented.
My default: terminate at the proxy for routing and header control, bridge only when a backend genuinely needs encrypted transport. Whatever you choose, pair it with certificate expiry monitoring, because a correctly configured proxy still breaks the day its certificate lapses.
— Nick Phillips
Otterwatch: monitoring to complement configuration
Once your proxy and backend are talking correctly, the next failure mode isn’t a misconfigured header, it’s a certificate quietly expiring weeks from now. We built Otterwatch to watch exactly that: certificate expiry and basic reachability, with plain-language email alerts instead of a dashboard you have to remember to check.

It won’t fix a missing proxy_ssl_server_name directive, but it will tell you well before a cert runs out, which is the failure that tends to sneak past everyone focused on config. You can run our free SSL certificate checker on any hostname right now, and monitoring up to five domains stays free with no credit card required. If you manage more sites or want deeper change-detection, the Pro plan runs $15 per month.
FAQ
Why is SSL no longer used?
“SSL” as a protocol name is mostly outdated; modern connections use TLS, which succeeded it after SSL’s versions were found to have serious weaknesses. People still say “SSL” colloquially to mean the certificate and encryption setup, even though the underlying protocol running is TLS.
Can a reverse proxy be hacked?
A reverse proxy is software running on a server, so it can have vulnerabilities like any other service, particularly around misconfigured TLS, outdated software, or exposed admin interfaces. Keeping the proxy patched, restricting administrative access, and verifying certificate chains with tools like openssl s_client reduces that risk considerably.
What does “SSL proxy” mean?
An SSL proxy, more accurately a TLS proxy, is a server that handles encrypted connections on behalf of a backend, either by terminating TLS and forwarding plain HTTP, or by passing the encrypted stream through untouched. The term covers both termination and passthrough setups, so the specific behavior depends on how it’s configured.
How secure is a reverse proxy?
A reverse proxy’s security depends entirely on its configuration: proper TLS settings, correct header forwarding, and hostname verification between proxy and backend. A well-configured proxy with SNI checks and trusted CA bundles is a standard, widely used part of secure architecture rather than a weak point by itself.
Sources
For the exact header fields and formatting, the X-Forwarded-Proto and Forwarded reference pages on MDN cover syntax and usage notes. For proxy-level directives, the nginx proxy module docs list every proxy_ssl_* option, and Apache’s mod_ssl documentation covers equivalent settings for mod_proxy setups. For load-balancer-specific framing of termination versus passthrough, AWS’s X-Forwarded headers guide walks through the Layer 7 implications of each mode.
Recommended
- SSL Debugging Checklist for Developers: 2026 Guide
- Fast DNS to CA Troubleshooting for SSL Breaks with curl and openssl
- Signs Your SSL Certificate Is Failing: 8 Clear Warnings
- Reading an SSL certificate with openssl — a tactical reference
Catch the next cert expiry before your users do.
Otterwatch checks your SSL certificates daily and emails you 30 days before they expire. Five sites free.
Start watching →