Configure Nginx to proxy requests to an HTTPS upstream
Encrypt the upstream connection, verify its certificate, and configure SNI and the HTTP Host header deliberately.
Use an https:// upstream URL and explicitly enable certificate verification. Nginx can terminate the client's TLS connection and create a separate TLS connection to the backend. Both network hops are encrypted, but Nginx can still read the traffic between them.
Configure TLS on both hops
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
location / {
proxy_pass https://internal-app.example.net:8443;
proxy_ssl_server_name on;
proxy_ssl_name internal-app.example.net;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/ssl/internal-ca.pem;
proxy_ssl_verify_depth 2;
proxy_set_header Host internal-app.example.net;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Supply real certificate and trust-bundle paths. Choose the verification depth for the actual chain. The backend must run TLS on the specified port and present a certificate valid for internal-app.example.net.
SNI selects the backend's TLS virtual host; the HTTP Host header selects its HTTP virtual host. They are separate settings. If the application expects the public host, use proxy_set_header Host $host instead and ensure that host is accepted by the backend. This does not change the certificate name used for verification.
Trust private certificates explicitly
Nginx's proxy_ssl_verify defaults to off; merely switching to HTTPS does not authenticate the upstream. For an internal CA, install the appropriate CA certificate bundle. For a self-signed endpoint, explicitly trusting the intended certificate can be appropriate for a controlled setup, with a renewal plan.
proxy_ssl_verify off leaves encryption without upstream identity verification. Do not use it as the permanent answer to a broken certificate chain.
Check names, upstream groups, and TLS versions
When proxy_pass refers to a group such as https://app_pool, set proxy_ssl_name to the backend's certificate name rather than assuming the group label appears in its certificate. Enable SNI when the upstream uses it to select a virtual host. Missing SNI can select a different certificate or endpoint; it does not inevitably fail if the default certificate already matches.
If protocol negotiation fails, compare supported versions at both ends. For a compatible build and backend, an explicit setting is:
proxy_ssl_protocols TLSv1.2 TLSv1.3;
Test the same name and trust path independently:
openssl s_client -connect internal-app.example.net:8443 \
-servername internal-app.example.net \
-CAfile /etc/nginx/ssl/internal-ca.pem \
-verify_hostname internal-app.example.net -verify_return_error
Preserve the full Nginx error and upstream address. Guance's Nginx integration can collect configured logs; verify parsing before relying on fields to distinguish certificate errors from negotiation failures.
FAQ
Does the upstream need its own certificate?
Yes. The frontend certificate does not automatically configure the backend. A private CA is sufficient when Nginx trusts it and verifies the expected name.
Can the public side use HTTP while the upstream uses HTTPS?
Yes, but only the upstream hop is encrypted. For a public service, redirect clients to a correctly configured HTTPS endpoint.
When is upstream TLS useful?
Use it when traffic crosses an untrusted network or your requirements call for encrypted, authenticated hops. Even on a private network, assess exposure and certificate-management costs explicitly; a private address alone does not establish trust.
Reference: Nginx proxy TLS directives.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan