Hazem Allbabidi

April 30, 2025 | 10 min read


Nginx Crash Course

First, we started by answering the question of: “What is Nginx?”.

Nginx is an HTTP web server and reverse proxy, built using C and designed to help be the front-facing part of your web applications, whether these applications were static websites and dynamic web applications.

Nginx can do much more than that. It can act as a load balancer, it can cache content such as images, it can handle WebSockets, and more.

In this crash course, we will begin by installing Nginx on a Linux server, then, we will go through the the configuration files, and finally, we will deploy our own custom web application.

Prerequisites

In order to complete this tutorial, you will need the following:

Installation

To install Nginx, you first update your system packaages:

sudo apt update

And then simply install Nginx:

sudo apt install nginx

Once the command is complete, you can open your browser and head to your servers IP address, you should a page with the heading “Welcome to nginx!”:

Welcome to nginx page

There are three important changes that we need to know before we can continue:

  1. A binary nginx has been installed. This binary will be used to run nginx as a service and can be used to check whether a configuration file is correct or not
  2. A new service has been created that runs the nginx binary in the background and allows access to the configured application(s) (we can check the service by running sudo systemctl status nginx)
  3. A new directory has been created on the path /etc/nginx, this directory includes all configuration files for Nginx

Since the installation already completed the setup of Nginx as a service, we can go to the next step of understanding the configuration files.

Nginx Configuration Files

If we list the items in the path /etc/nginx, we will see multiple different files and directories:

# ls /etc/nginx/
conf.d        fastcgi_params  koi-win     modules-available  nginx.conf    scgi_params      sites-enabled  uwsgi_params
fastcgi.conf  koi-utf         mime.types  modules-enabled    proxy_params  sites-available  snippets       win-utf

We will only focus on 3 files and directories in tihs tutorial.

The first file, is the nginx.conf. This is the main entry file for the nginx config. It specifies the default configs for all systems deployed through Nginx, such as the error log path, the access log path, the user used to access and run the applications, and more.

Nginx configuration files are consisting of key-value pair and objects (referred to as contexts or blocks) that include there key-value pairs.

If we take a look at the nginx.conf, the first few lines should look like this:

# head /etc/nginx/nginx.conf 
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;

The key represents the main config we are trying to set

For example, in the 4th line, we can see the key error_log, this config allows us to set a path for where the error will be logged to.

Right beside it, after a “space”, we can see the value for that config, which represents the path for the error logs in this case, which is /var/log/nginx/error.log.

If we go down in the file a bit more, we will see a large object:

http {
        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        types_hash_max_size 2048;
        # server_tokens off;

        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

        access_log /var/log/nginx/access.log;

    ...
}

This context represents the main entry for the Nginx configuration, we can see key-value pairs such as the access_log. If you checkout the end of that block, you should see the following line:

include /etc/nginx/sites-enabled/*;

This “imports” the files in the path /etc/nginx/sites-enabled, which is the directory we will look at next.

Sites Configuration Files

There are actually two important directories we need to look at:

There two directories contain the configuration files for your different applications. You can have a different configuration file for each application you are deploying behind the Nginx reverse proxy.

The main difference between these two files is that the sites-available directory contains all the config files that can be used, while the sites-enabled directory only contains the “active” config files that should be enabled.

Usually, users create the configuration file for their purpose and save it in the sites-available directory, then create a symlink to it and place it in the sites-enabled directory, this helps users from having to copy files and mess up by keeping different copies of the same file or the same files with different content.

Note: a symlink, short for symbolic link, is a shortcut or a copy of a file

For the sake of this tutorial, we do not need to worry too much about these two directories because we only need 1 file in order to deploy our application, and it already exists in these directories under the name default.

Before we start editing our config file, let us first take a look at the default file.

We will print out the file and exclude the comments (which start with #) for simplicity:

# cat /etc/nginx/sites-available/default | grep -v '#'

server {
        listen 80 default_server;
        listen [::]:80 default_server;


        root /var/www/html;

        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                try_files $uri $uri/ =404;
        }
}

There is one context server in this file, which represents a web server that allows access to an application.

Inside this context, we first see a listen key with the value 80 which represents the port on which this web server is listening on. The default_server directive tells the server that this is default we server for the mentioned port.

The next directive is the same as the previous one, which listens on a port (80 in this case), but on IPv6.

Next, there is the directive root which represents directory for our application, in this case, the root directory is /var/www/html, which if we list the contents of, we see the file index.nginx-debian.html, which is the “Welcome to nginx!” HTML file.

Next is the index, or starting, file for our app, there are multiple file names listed here, which represent the different file names of the main starting file for our app.

Next, we see the directive server_name which has the value _. This directive is what specifies the domain name for our application. For example, if our domain name was google.com, the line would be:

server_name google.com;

Lastly we see another context:

location / {
        try_files $uri $uri/ =404;
}

This represents the location of which a user might access when going to our application. In this case, anything that starts with / will have the listen config. In this case, we have the directive:

try_files $uri $uri/ =404;

This basically tells Nginx to try to access the URI taken from the request (e.g. if the request was to example.com/file, the URI value, or $uri variable, will be /file) and find the requested file. If the file does not exist, it will display the 404 page.

Now that we understand Nginx configurations, we can start setting up our app.

App Deployment

In this section, we will begin by creating a simple Node JS server and then deploying it using Nginx by editing the configuration files.

A Simple Node JS App

We will create a new Node JS app that serves a simple HTML that shows the time.

We will begin by installing NVM, or Node Version Manager:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

This pulls the installation script and runs it using bash.

Next, we need to close and reopen the terminal in order to get access to nvm.

Now we can install the latest version of Node JS using nvm, we can do that by running:

nvm install node

We can finally start building our app. We first create a new directory that will contain our app code, then access it:

mkdir node-app
cd node-app

Next, create a new file called server.js and add the following content to the file:

let http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end(`Hello World. The current date and time is: ${new Date().toLocaleString()}\n`);
}).listen(3000);
console.log('Server running on port 3000');

This will use the standard library http package to create a server that responds with the message Hello World.... It will listen on port 3000.

To run this app, we will install and use another tool which allows us to run Node JS apps in the background, this tool is called: pm2.

To install pm2 globally, run:

npm install pm2 -g

To start the app in the background using pm2, simply run:

pm2 start server.js

We should have our app running on port 3000!

Editing The Nginx Configuration

Before we begin, let us identify what we need to do.

The goal is to have our app be accessible through port 80 on our servers IP Address. To do that, we have to forward any requests that come through port 80 to the application which is running locally on port 3000.

To do this, let us first empty out the default file in the sites-available directory:

echo "" > /etc/nginx/sites-available/default

Next, open the file up for editing.

We need to first create a server context, which will contain all necessary details for accessing the application:

server {

}

Next, we need to set the port in which the application will be accessed through. The ports used are going to be either port 80 or 443 if we are using SSL. In our case, for learning and testing purposes only, we will use port 80:

server {
    listen 80 default_server;
}

Note: We do not need access through IPv6 therefore we don’t need to listen to port 80 on IPv6.

Next, we need to accept requests on the / location or path, which will allow access for any page on our app. While we currently only have 1 page, if we decide to add more pages later, we won’t need to change the Nginx configuration, requests happening on any path (e.g. /test) will still be forwarded to our application.

server {
    listen 80 default_server;

    location / {

    }
}

Finally, we need to forward all requests coming to our application. Since our application is running locally and on port 3000, we will need use the directive/key proxy_pass. This directive forwards any requests going on the contexts location, which is / in our case, to a running application server, which is the Node JS app in this case.

The proxy_pass key will have the value of localhost, along with the app port of 3000:

server {
    listen 80 default_server;

    location / {
        proxy_pass http://localhost:3000;
    }
}

Now just save the file and we are done!

The final step is to reload Nginx in order for it to start reading the new configuration file:

sudo nginx -s reload

Assuming you get no issues, you can now head over to your browser and insert the servers IP Address. You should see the current time of the server.

Final Notes

You might have noticed there are multiple differences between the original default file and our one. The reason is due to the type of application we are deploying in each case.

In the original file, the app was just a single HTML with some content (i.e. “Welcome to nginx!”), that is the reason we had the following directives:

In our new config, we did not need to deploy any HTML, therefore, we did not need to add any of the above directives. But we did have an app running on port 3000, which means we had to forward all our requests to that app. That is why we used a proxy_pass directive, to basically forward all requests coming to port 80 onto the app running on localhost on port 3000.

Conclusion

Nginx is a very useful tool and is being used at major companies such as Netflix, AirBnB, Github, and many more.

What we went through in this tutorial was only scratching the surface of what Nginx can do. With more learning and more practice, you will be able to use it for more advanced deployments and for its other features like content caching.

Thank you for reading, I hope you learned something new. See you in the next one!


Previous

DNS For Software Developers

Next

Security For Software Developers
Sign Up To Binance To Get 10% Off Commission Fees Sign Up To Kucoin