Nginx – How to enable TLS when accessing a proxied server in http mode

nginxssl

I am using a Nginx server as a reverse proxy. It serves both HTTP and HTTPS traffic and dispatches the calls to multiple HTTP-only websites.

Recently, I wanted to be able to encrypt the traffic between Nginx and the underlying server. Nginx still decrypts the HTTPS traffic it receives from the Internet just like before, but then should use HTTPS with a different certificate to access the underlying server.

I started to change the configuration, following the official documentation. However, proxy_ssl on can be set only within a stream level, and not http. This is confirmed by nginx -t which shows for the line containing proxy_ssl on; the following error:

"proxy_ssl" directive is not allowed here […]

My difficulty is that I'm using Nginx to log traffic, add headers, etc., so I have to use http. If my understanding is correct, stream level is needed when Nginx is used to transmit HTTPS traffic directly, without the ability to decrypt it (i.e. SSL Pass-thru), which is not my case.

Can I make Nginx connect through HTTPS to the underlying server while still using http level and all its benefits such as logging and changing headers?

Current configuration:

http {
    proxy_set_header ... # Logging and custom headers.

    ...

    upstream demo-failover {
        server demo1.example.com:443 weight=1000 max_fails=0;
        server demo2.example.com:443 backup;
    }

    server {
        listen 80 proxy_protocol;
        listen 443 ssl http2 proxy_protocol;
        server_name demo.example.com;

        # HTTPS configuration for public exchange (Internet <-> Nginx).
        ssl_certificate /.../fullchain.pem;
        ssl_certificate_key /.../privkey.pem;

        ssl_stapling on;
        ssl_stapling_verify on;

        # HTTPS configuration for private exchange (Nginx <-> underlying server).
        proxy_ssl on; # This is the line which is invalid.
        proxy_ssl_trusted_certificate /.../ca.pem;
        proxy_ssl_certificate /.../demo.pem;
        proxy_ssl_certificate_key /.../demo.key;
        proxy_ssl_verify on;
        proxy_ssl_verify_depth 2;
        proxy_ssl_session_reuse on;

        if ($scheme = http) {
            return 301 https://$server_name$request_uri;
        }

        location / {
            proxy_pass http://demo-failover;
            proxy_next_upstream error timeout invalid_header;
            proxy_connect_timeout 2;
            proxy_redirect off;
            proxy_http_version 1.1;
        }
    }
}

Best Answer

Found it.

There is no need to switch to stream, and there is no need to use proxy_ssl on.

A different example from the official documentation shows how to use HTTPS at http level. The trick (and that was my mistake when I tried to remove proxy_ssl on and found myself with Nginx trying to call the underlying server using HTTP) is the proxy_pass value. In my case, I kept the original value:

proxy_pass http://demo-failover;

Instead, I should have changed it to https:

proxy_pass https://demo-failover;
#              ^