You are currently viewing Learn Nginx and its basics in 2022
Nginx tutorial

Learn Nginx and its basics in 2022

A few weeks ago, I had to do a complex redirect of a request e.g if the request coming to nginx has a specific query param or is coming from a specific host then internally redirect to a different path. I had completed my logic and was certain about its working but as the saying goes — Your software will eventually fail unless it’s verified with all the edge cases.

However, due to our system dependency, I cannot merge the code to staging dev for testing as in case of nginx failure, it will block other developers to write/testing their node API or client code. 

So, to mitigate this, I have set up the nginx in my local computer and did thorough testing. Once it’s fine locally, the code is ready to be pushed in staging and further testing. This way I save lots of time without hampering others’ work.

In this article, I’ll walk through the basics of nginx, installation, and set up locally, setting up logs, and a few others.

Let’s start with the definition of nginx.

What is Nginx?

Nginx is a short form of engine x is an HTTP and reverse proxy server. It is heavily used as a load balancer and serves static files, sometimes even complete static websites like the company’s blog hosted on Nginx!

Load balancer

In simple terms, the Load balancer is like a middle man sitting in between the concerned parties e.g assume A is the list of clients and B is the list of the servers then — 

Case 1: With no Load balancer

All incoming requests would be going to just one server which in the worst case, makes it hang or crash. You probably have heard the term ‘Node API or Service API is down’ which means the box or the server serving that API request is hung or crashed due to request overload or OutOfMemory etc. Thus making the UX unresponsive.

Case 2: With Load balancer

All incoming requests will have to go through the Load Balancer. It maintains the routing table and gets notification if any of the boxes or server goes down (through polling). It efficiently distributes the network requests across servers and if one server goes down it redirects the requests to others servers that are online. Thus, maintaining the availability of the server always online. 

Nginx Configuration File

This file is a tree-like structure and contains the instructions in the form of rules/blocks. 

				
					# main context (outside any other context i.e global context) 

# event context 

event { 
    worker_connections 1024; 
} 

#http context 

http { 
    # server context 
    
    server { 
        # code goes here 
    } 
    
    server { 
        # another server context, code goes here 
    } 
}
				
			

Before we dive into creating our own web server, let’s learn the Nginx configuration file structure in a crisp mode –

Main Context 

The `main context a.k.a. global context` is the topmost context and all other contexts are part of it e.g `Event context, HTTP context`. It is used to configure details that affect the entire application on a granular level.

Event Context

Event Context is contained within the `Main context`. It deals with connection handling in general. All the directives defined within this context deals with how worker processes should handle the incoming connections.

HTTP Context

This is the sibling of the Event context and written side-by-side of the event context rather than nested.

This context will hold the majority of the configurations if we are using Nginx as a web server or reverse proxy. 

Note:- 

There can only be one event context and HTTP context within the nginx configuration.

Later in the article, We will see 2 more contexts — server context and location context.

How to install Nginx in macOS?

If you are not using brew then install it first. Open your terminal and do the followings —  

install brew

				
					/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
				
			

Once the brew is installed, do 

				
					brew install nginx
				
			

Once nginx is installed, you can verify it by 

				
					nginx -v
				
			

Above should print nginx version: nginx/<some version number>

				
					e.g.
nginx version: nginx/1.21.0
				
			

Once nginx is installed, the brew will create the nginx folder at the below location

				
					/usr/local/etc/nginx
				
			

the default nginx.conf will look like this –

				
					events {
    worker_connections 1024;
}

http {
    server {
        listen       8080;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
    }
}
				
			

To start the nginx services do the following — 

				
					nginx
				
			

if there is any error it will be logged in the terminal, to test if it is serving the default HTML file, hit the URL 

				
					http://localhost:8080
				
			

and to stop it — 

				
					nginx -s stop
				
			

How to serve files from a different location?

Let’s modify the nginx.conf file to read the HTML file from a different location  — 

First, create a folder that contains the HTML file index.html (with below content) that you want to serve e.g I’ve created nginx-poc inside the Download folder.

				
					<!DOCTYPE html>
<html lang="en">
<head>
    
    <meta charset="UTF-8">
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <title>Document</title>
</head>

<body>
    <h1>This is index html file from nginx-poc folder</h1>
</body>
</html>
				
			

Copy the complete path of this folder that will be the root and open the nginx.conf to update it

using vim editor or nano or any other way you prefer to open the file (e.g nano) — 

				
					nano /usr/local/etc/nginx/nginx.conf
				
			

and update the root location 

				
					events {
    worker_connections 1024;
}

http {
    server {
        listen       8080;
        server_name  localhost;
        location / {
            root /Users/Download/nginx-poc;
            index  index.html index.htm;
        }
    }
}
				
			

Now stop the nginx and start it again to see the updated changes. Hit the localhost URL with port 8080 –

First Testing

How to do Url redirection?

Sometimes, the need may arise for URL redirection when the existing resource is moved to a different location. Let’s see how can we achieve this — 

Create a js folder and a few js files in it inside the nginx-poc folder — 

				
					|-- nginx-poc
| |-- index.html
| |-- js
| | |-- a.js
| | |-- b.js
| | |-- c.js
| | |-- b-latest.js
				
			
Directory structure

Just have one simple console.log(<filename>) inside each js file –

e.g for a.js

				
					console.log('serving a.js');
				
			

for b-latest.js

				
					console.log('serving b-latest.js');
				
			

and so on.

Let’s say the file b.js is no longer useful and we want to serve the b-latest.js in place of it. Of course, we can say that wherever our anchor link is pointing to b.js we’ll replace it with the b-latest.js, but it has 2 issues –

  1. It is time-consuming and is error-prone.
  2. The old URL will give the 404 and that’s something we should thrive to reduce. Ideally, there shouldn’t be any 404 redirects, it should be kept as low as possible.

One simple solution would be to do it from nginx internal redirect — 

				
					events {
  worker_connections 1024;
}
http {
  server {
    listen       8080;
    server_name  localhost;
    root   /Users/Download/nginx-poc;
    location /js {
      rewrite /js/b.js /js/b-latest.js break;
    }
    location / {
      # root   /Users/Download/nginx-poc;
      index  index.html index.htm;
    }
  }
}
				
			

The highlighted ones are the new changes, let me go through each change to make it clearer — 

  1. Commented root in location / —  This is moved to the server context. Adding the root to the server context makes it available for all the location contexts within it.
  2. Added location context to handle the /js request — This request will handle all the request coming from /js, /js/* i.e request for /js/b.js will fall in this location. We are rewriting the request URL internally from /js/b.js to /js/b-latest.js and then we are adding a break which means no more parsing of any other rewrite!

Note: —  

  1. The server context is a child of the HTTP context. There could be multiple server contexts, unlike event and HTTP context which are allow allowed once. 
  2. The location context is a child of the server context. Similar to server context, multiple location contexts are allowed. They are the ones where actual handling of the incoming request is done.

How to add custom logs?

Logs are really important, they are needed to test our logic. In case any issue comes in production code, we can easily debug by seeing the nginx logs. Let me show you how can we set it up locally so that we can test and view the complete logic along with logs in localhost. 

By default, nginx has 2 types of logs — access log and error log

Access log

This logs the visitor’s activity e.g requested URL, IP addresses, host, referrer, etc. If a request is served successfully it will log in access.log file.

				
					access_log 'location of log file' log_format_name;
				
			

In log_format, we can add what data we want to log but just a note, it is an optional thing. One important point to remember is log_format will be placed under the HTTP context otherwise it will throw an error.

e.g.

				
					Syntax - log_format log_format_name string;

log_format custom '$remote_addr - $remote_user [$time_local] '
'"$request" "$uri" $status $body_bytes_sent ''"$http_referer" "$http_user_agent" "$gzip_ratio"';
				
			
Example 1 Nginx

Error log

This reports each glitch and Syslog i.e if a request was not served by any means it will be recorded in the error.log file. 

				
					Syntax - error_log 'location of error.log' file> 'error severity level'

e.g.

error_log /usr/local/etc/nginx/logs/error.log notice;
				
			

Configuration file after adding the logs — 

				
					events {
  worker_connections 1024;
}

http {
  log_format custom '$remote_addr - $remote_user [$time_local] '
      '"$request" "$uri" $status $body_bytes_sent '
      '"$http_referer" "$http_user_agent" "$gzip_ratio"';
  
  server {
    
     listen       80;
    
     server_name  localhost;
    
     root   /Users/Downloads/nginx-poc;
     
     access_log /usr/local/etc/nginx/logs/acess.log custom;
     error_log /usr/local/etc/nginx/logs/error.log notice;
     rewrite_log on;
     
     location /js {
       rewrite /js/b.js /js/b-latest.js break;
     }
     
     location / {
       index  index.html index.htm;
     }

   }
}
				
			

The rewrite_log should be ON to record the internal redirect. Also, notice severity level means it is just a notice which can be simply ignored i.e nothing serious.

Nginx error log

How to handle query params?

It may arise a case when we want to internally redirect the request based on query parameters. Let’s see how can we implement the below 3 cases in the nginx.conf file — 

				
					events {
  worker_connections 1024;
}

http {
  log_format custom '$remote_addr - $remote_user [$time_local] '
      '"$request" "$uri" $status $body_bytes_sent '
      '"$http_referer" "$http_user_agent" "$gzip_ratio"';
  server {
    
     listen       80;
    
     server_name  localhost;
    
     root   /Users/Downloads/nginx-poc;
     
     access_log /usr/local/etc/nginx/logs/acess.log custom;
     error_log /usr/local/etc/nginx/logs/error.log notice;
     rewrite_log on;
     
     location /js {
       if ($query_string ~* "latest=true") {
         rewrite /js/b.js /js/b-latest.js break;
       }
       if ($query_string ~* "latest=false") {
         rewrite /js/b.js /js/c.js  break;
       }
       rewrite /js/b.js /js/a.js break;
     }
     
     location / {
       index  index.html index.htm;
     }
     
   }
}
				
			

Case 1

request is for b.js → serve b-latest.js iff query params have latest=true

Case 1 Nginx

Case 2

request is for b.js → serve c.js iff query params have latest=false

Nginx Case 2

Case 3

request is for b.js → serve a.js default

Nginx Default case

Conclusion

Nginx is not just it, and can’t be covered in just one article. But, I hope this will let you get started to know more. With the local setup, It becomes really handy when you want to test your nginx logic locally before deploying it to your staging or production.

I really hope you like the article, if yes, please follow me and if possible buy me a coffee. This article is also published on medium, keep visiting that also for regular updates.


Please share the articles on –

Facebook
Twitter
LinkedIn
Subscribe

The latest tips and news from the industry straight to your inbox!

Join 30,000+ subscribers for exclusive access to our monthly news and tutorials!

This Post Has One Comment

Comments are closed.