Skip to content

Nginx SSL Reverse Proxy

   

In this post I would like to show a minimal configuration to set up a Nginx server with SSL termination and reverse proxy to another service. Also, I set up a static html content serving.

Here you can find the complete config. Read further for an explanation.

So, why would I want to set up a reverse proxy with ssl termination?

The main reason I do this, is because in this way I don’t need to handle the secure connection in the service itself. So, no matter how you develop your service (Java/Spring, GoLang, C/C++, etc.) you just need to provide a simple http interface and let the proxy handle the ssl/tls part.

Schema

In this particular example, besides the ssl termination, I will also add the possibility of serving static http content.
So the Ngnix server will also do routing based on path. Certain requests to the proxied service and others to index.html.

A basic diagram of what I will set up:

Schema

Generating the self signed certificates

For testing/dev purposes, I will generate self signed certificates. This should be enough for a dev environment.
I followed the method listed in here.

In short, you need to generate the certificates using openssl and provide a configuration file for the certificate.

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./ssl/private/localhost.key -out ./ssl/certs/localhost.crt -config ./ssl/localhost.conf

You can get an example content for the localhost.conf here.

Nginx configuration (nginx.conf)

events { }
http {

	server {

		listen 80;
		listen 443 ssl http2;
		listen [::]:443 ssl http2; #for IPv6

		server_name localhost;

        #specify the certificate files to use
		ssl_certificate /etc/ssl/certs/localhost.crt;
		ssl_certificate_key /etc/ssl/private/localhost.key;

		ssl_protocols TLSv1.2 TLSv1.1 TLSv1;

		root /usr/share/nginx/html;

        #Serving index.html file when requesting /
		index index.html;
		location / {
		}

        #Reverse proxy for requests to /echo
		location /echo {
                proxy_set_header        Host $host:8080;
                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;

				proxy_pass http://localhost:8080/; #if running outside docker you can use 127.0.0.1 or localhost instead of host.docker.internal, or with docker with network_mode: "host"
				proxy_redirect off;
			    }
		}
}

Putting it all together as a docker compose configuration

For convenience, I use docker-compose to spin up a Nginx server and an echo test service.

This is the docker-compose.yml configuration:

version: "3.5"
services:
  https-proxy:
    image: nginx
    network_mode: "host"
    ports:
      - 8081:8081
      - 443:443
      - 80:80

    volumes:
      - ./nginx:/etc/nginx:ro
      - ./ssl:/etc/ssl:ro
      - ./html:/usr/share/nginx/html:ro

  echo-service:
    image: vad1mo/hello-world-rest
    ports:
      - 8080:5050

With that, I specify the Nginx image to expose the necessary ports and mount some local folders as volumes. From these folders Nginx will pick up the configuration, the certificates I generated and the static html content to serve.

Also, I set up the echo service to serve on port 8080.

An important detail here, when using nginx in a docker container it’s useful to set the config “network_mode: “host”. In this mode the container shares the networking namespace of the host, directly exposing it to the outside world. This allows referencing “localhost” directly from nginx.conf file.

To spin up this docker setup:

docker-compose up https-proxy echo-service  

The complete code and scripts to set up the explained configuration can be found here https://github.com/miguelabate/nginx-ssl-reverse-proxy

Conclusion

In this article I showed a simple setup for a Nginx reverse proxy with ssl termination. This is not only useful for production architectures, but also for development setups since modern browsers put restrictions if you want to call non tls/ssl endpoints from non tls/ssl endpoints (a common occurrence during local testing). Also, browsers restrict the access to user GPS location when using non secure connections.
For these reasons I think it’s an overall good idea to set up a secured ssl development environment that later can be moved without many modifications to production.