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/test 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 for 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.
The 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
-
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
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 -
- It is time-consuming and is error-prone.
- 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;
}
}
}
Let me go through each change to make it clearer —
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.
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 abreak
which means no more parsing of any other rewrite!
Note:—
The
server context
is a child of theHTTP context
. There could be multiple server contexts, unlike event and HTTP context which are allow allowed once.The
location context
is a child of theserver 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;
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 must 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"';
Error log —
This logs 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>
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.
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 2 —
request is for b.js
→ serve c.js
iff query params have latest=false
Case 3 —
request is for b.js
→ serve a.js
default
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 originally published on my website, keep visiting that also for regular updates.
Thank You! Stay tuned for more articles.
Top comments (7)
Might have to learn this at some point thanks for the share.
Nginx is cool, but you're only scratching the surface until you switch to openresty 😝
I always wanted to learn what nginx was. You explained it simpler and very elegantly. Thank you!
A pretty nice to ol to easily test nginx config files nginx-playground.wizardzines.com/
Really great run through, thanks for the content
Great stuff!
Thanks PsySolix