Configuring Nginx as a reverse proxy web server (including SSL!!!)

Updated: August 11, 2024

Use suitable configurations and publish your web apps like a boss.

Nginx and Apache are two of the most popular http servers. While the latter is more appropriate for dealing with static ../media and has its configuration file based on an XML structure, the former is better suited for handling dynamic press. Its configuration file is based on a JSON-like structure (although it is not a JSON file). Both can serve multiple apps on the same server by providing a reverse proxy approach; in other words, Nginx and Apache can be set up to receive requests from a client and forward each request to a different application running on a server's internal port. Coming from a Ruby on Rails background, I've been using Nginx for many years because it is the most common http server used to serve Rails apps worldwide. Therefore, I have some tips for setting up your Nginx and its configuration file like a boss.

First, let's install it. There are two main ways to install it as a package or use it in a docker container. First things first, to install it as a package on your operating system, assuming you're using an Ubuntu 20.04, type:


$ sudo apt update
$ sudo apt install nginx
              

Alternatively, to get it running a docker container, there are a lot of docker images throughout the internet for Nginx. Personally, this one is good: Nginx V.1.19 on Alpine (Make sure you can run it by passing the nginx. Conf from a volume with docker run -d -p 80:80 --read-only -v $(pwd)/nginx-cache:/var/cache/nginx -v $(pwd)/nginx-pid:/var/run nginx or `volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro` in a docker-compose.yml file). If you installed it on Ubuntu, you would be able to run a command to confirm that Nginx is really running by typing service nginx status on your terminal, accessing "localhost" on your browser, or making a curl request against it. You can install it on your local or inside a docker container and you can change a crucial file: nginx. Conf. Here is where the magic happens. In the recommended docker image, you can change a local nginx. The conf version is referenced inside the container through a volume link. To open your nginx.conf, assuming again that you installed it locally, you can choose your favorite text editor, such as nvim/vim, and open it as a super user because Nginx requires that:

$ sudo vim /etc/nginx/nginx.conf
              

Then, you shall find in the first five lines a having the following lines:

# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
# ...
              

In these lines, only taking care of the user name is necessary. Avoid setting it as "root" or your machine's primary user. It is possible to set a password, but leave it for another blog post because this is a basic overview. Then, you can go to the section http { ... }. You can add only a few new lines, except to the compressing section, although we will add two server sections. Before discussing the server sections, let's add some configurations for the compression, making your web application faster if used correctly. You can add/uncomment a section with some gzip configurations (be careful not to add ../media files such as .png because they consume too much CPU time).

# /etc/nginx/nginx.conf
http {

#...
        ##
        # Gzip Settings
        ##

        gzip on;
        gzip_disable "msie6";

        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_types text/plain text/css application/json application/javascript
        text/xml application/xml application/xml+rss text/javascript;

#...
}
              

After that you will be able to set up the server sections inside the http parent-section. Considering you would like to provide a https-only site, you need to buy a SSL-certificate and generate the ssl-bundle.crt and .key file (there is another file called trusted.crt, but you are not obligated to add it). This source teaches you how to generate these files if you are using Comodo certificates. Next, you need to reference these files to add two server sections. The first is for receiving http-only requests and redirecting to the https requests on the same server. The second is precisely for handling the https-only requests. In this second server section, we are also adding a Location sub-section to handle the rever-proxy forwarding process to an application running on the port 3001 of your localhost. Therefore, any web application running on the port 3001 will receive requests to the www.myawesomesite.awesome. Have a look at the comments.

# /etc/nginx/nginx.conf
http {

#...

        ##
        # SSL Settings
        ##
        server {
                listen 80;
                listen [::]:80;
                server_name example.com www.myawesomesite.awesome;
                return 301 https://www.myawesomesite.awesome$request_uri;
        }

        server {
                listen 443 ssl http2 default_server;
                listen [::]:443 ssl http2 default_server;
                ssl on;
                server_name www.myawesomesite.awesome;

                # Here you can see the most important configs for the ssl
                ssl_certificate /etc/nginx/ssl/www.myawesomesite.awesome/ssl-bundle.crt;
                ssl_certificate_key /etc/nginx/ssl/www.myawesomesite.awesome/www.myawesomesite.awesome.key;
                ssl_trusted_certificate /etc/nginx/ssl/www.myawesomesite.awesome/trusted.crt;

                ssl_session_timeout 1d;
                ssl_session_cache shared:SSL:20m;
                ssl_session_tickets off;

                ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

                ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:
                ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:
                DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:
                ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:
                ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:
                ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:
                DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:
                DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';

                ssl_stapling on;
                ssl_stapling_verify on;

                # Some useful setings for the reverse proxy app
                proxy_set_header    Host              $host;
                proxy_set_header    X-Real-IP         $remote_addr;
                proxy_set_header    X-Forwarded-For   $proxy_add_x_forwarded_for;
                proxy_set_header    X-Forwarded-SSL on;
                proxy_set_header    X-Forwarded-Proto $scheme;

                # Finally, the following lines set up the reverse proxy fom the localtion / to the
                # app running in the port 3001
                location / {
                        proxy_set_header Host $http_host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_pass "http://localhost:3001/";
                }

        #...
        }
#...
}
              

If you did everything somewhat, you can validate your nginx.conf, check the logs (optional) and restart Nginx to get the new nginx.conf running. Try the following commands:

$ sudo /usr/sbin/nginx -c /etc/nginx/nginx.conf -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
$ sudo journalctl -xe
$ sudo /etc/init.d/nginx restart
              

Now, enjoy running your application. Stay tuned for the next "episodes" with more powerful settings to add to your nginx.conf.

Portfolio

I selected a few projects I've worked on to show you. Some projects have contract constraints so that I won't be showing them. I played the role of Frontend and Backend developer in all of them, sometimes in time, sometimes on my own.

Lillio's Observations Project: 2024

Lillo's Observations

At Lillio, I served as both the tech lead and full-stack engineer for the Observations project, developing a comprehensive feature using Ruby on Rails, ReactJS, React Native, PostgreSQL, and AWS. I managed the integration of a robust backend with a dynamic frontend to enhance functionality and mobile integration. More info here.

Lune: 2023

Conserve Piso

Payment management system for Cappta getway. This project was done in dotnet core, MSSQL SERVER and VueJS.

Ideallang English: 2022

Ideallang

English Course website made in WordPress and using a MySQL database (in partnership with another engineer).

Posto7: 2021

Ideallang

Nutrition application made with ReactJS, Firestore DB, and Firebase. My main attribution was to build an admin portal for the back office team

PSMI: 2020

PSMI

(Work in maintenance) Education platform containing a bunch of curses in Brazil, written in Ruby on Rails, PostgreSQL, Express JS, Google Cloud Functions, and AWS. I was responsible for upgrading Ruby and Rails versions, as well as transitioning the cloud infrastructure from GCP to AWS.

Mangue Tecnologia: 2020

Mangue Tecnologia

(Work in maintenance) An educational platform offering a variety of courses in Brazil, developed using Ruby on Rails, PHP, JavaScript, and MySQL. I built several eCommerce websites, enhancing user experience and functionality.

Chefstrela': 2019

Ideallang

Gastronomy E-commerce made in WordPress and using a MySQL database (in a partnership with another engineer and a designer)

Casa Roberto Marinho: 2018

ICRM

Content management system with Ruby on Rails, MySQL, and JQuery. Cultural and educational business.

Kariri Sapuyá: 2018

Kariri Sapuyá

Digital archive combined with a content management system for storing and : was delivering historical documents about South American Native peoples in Northeast Brazil. It is made with Ruby, Ruby on Rails, ElasticSearch, JQuery, MySQL, and MongoDB.Link to the web app: Kariri Sapuyá.

Perceive: 2017

Perceive

Automated tool for extracting useful information about vulnerabilities from mailing lists. It includes a series of solutions, including Python Jupyter Notebooks, a Flask server, and a MySQL database. More info here.

Conserve Piso: 2017

Conserve Piso

A web system developed using PHP, MySQL, and jQuery, designed to streamline the management of flooring projects. This system replaces traditional methods with a digital platform that enhances efficiency and accuracy.

DPMS (Digital Management System): 2015-2017

DPMS

System constructed with Ruby on Rails, MySQL, JQuery, and Telerik Kendo. It aims to manage all steps of an industrial project, replacing common spreadsheets with sophisticated, customizable, and reliable resources. See more info: See more info: Oxeanbits.

Portuguese Atlantic (Portuguese Name: Atlântico Português): 2014

Portuguese Atlantic

Content management system made with Ruby on Rails, PostgreSQL, JQuery, and ElasticSearch. It is an application to manage and search content in a digital historical archive, which is also uploaded within the CMS. Link to the web app: Atlântico Português.

About me

Hey! I'm a senior full-stack software engineer, Vinicius L. Gesteira (aka Vini). Since 2014, I've worked with web development (mainly Ruby, Ruby on Rails, Rust, Solidity, Node JS, Docker, AWS, React, and Svelte), machine learning, web scraping, Python scripts and other related fields. If you are interested in more details, please check out my LinkedIn and GitHub.