Introduction to NGINX
Tags: webdev, nginx, beginners, devops
Published: 2023-12-19 04:46:44

NGINX, pronounced as 'engine-x', is an open-source web server that is mainly used for Reverse proxy, Caching, and Load balancing. 

If you're into web dev, at some point in your career you might have to work with NGINX. This article will help you get started with NGINX. By the end, you'll be able to setup your own NGINX web server. You'll also understand terms like load balancing and reverse proxy.

---

### Why NGINX?

NGINX is widely used for serving web content. It is known for its high performance, stability, and efficiency, making it a popular choice for various web applications and websites.

Some key features of NGINX are:
1. **Web Server** 🤖: It can serve static content(such as HTML, CSS and images) directly to clients, making it efficient for delivering web pages.
2. **Reverse Proxy** ⏩: It can act a reverse proxy by forwarding client's requests to other servers. It is a commonly used practice for security and scalability.
3. **Load Balancer** ⚖️: It can route incoming traffic to different server instances ensuring optimal resource utilization.
4. **SSL/TLS Termination** 🔐: It can handle SSL/TLS termination, encrypting and decrypting data between clients and servers.
5. **Performance Optimization** ⚡️: It is designed to be lightweight and efficient, capable of handling a large number of concurrent connections with low resource usage.

---

### Installation

##### Ubuntu/Debian
```bash
sudo apt update
sudo apt install nginx
```

After installation, NGINX will automatically start in the background. To check the status, run:
```bash
sudo systemctl status nginx
```

##### MacOS
We can install it using brew
```bash
brew install nginx
```

After installation, you can run it using:
```bash
brew services start nginx
```

##### Windows
1. Download the latest stable version of NGINX from the official website: [NGINX Download](https://nginx.org/en/download.html).
2. Extract the downloaded zip file to a location of your choice.
3. Navigate to the NGINX directory and run `nginx.exe`. NGINX should start.

---

### Configuration
To find the configuration file, run:
```bash
nginx -t

nginx: the configuration file /opt/homebrew/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /opt/homebrew/etc/nginx/nginx.conf test is successful
```

This command tests the configuration file for syntax errors and typos, without applying it. It will also reveal the path to the main configuration file.

You can either edit this file directly or create a new one to follow along.

#### Serving HTML

If you check the `nginx.conf` file, you'll see a lot of blocks wrapped in `{}` and a lot of key-value pairs inside these blocks.

These blocks are known as **contexts** and the key-value pairs are known as **directives** for that context.

We'll clear the file and start from scratch. Your `nginx.conf` should look like this:
```bash
events {}

http {
    server {
        listen 8080;
        root /Users/syfe/Desktop/tutorials/nginx-test/html;
    }
}
```

You only need `events` and `http` contexts to successfully load a configuration. Inside `http`, we have the `server` context that has the configuration for our web server.

It tells the server to `listen` on port `8080` and set `root` for serving files as `/Users/syfe/Desktop/tutorials/nginx-test/html`.

Inside the `html` directory, we'll create an `index.html` file.
```html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h1>Hello, world!</h1>
</body>

</html>
```

This is how directory structure looks currently.

![Directory structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ipipi48kr2usfaqc7cb3.png)


If you created a new conf file, you can load it by running:
```bash
nginx -c path_to_your_file
```

And after making changes to any conf file, you can apply it using:
```bash
nginx -s reload
```

If you go on `http://localhost:8080`, you'll see the html being served. 🎉 

#### Serving other static content
If you try to add styles to your HTML content using a CSS file and serve it using the current configuration it will not work.

To make this work, you have to set the correct mime types in the configuration.

```bash
...
http {
  types {
    text/css   css;
    text/html  html;
  }

  server {
    ...
  }
}
...
```

But manually adding these mime types for all different kinds of assets is cumbersome. Therefore, NGINX comes with a default `mime.types` file that can be used here.

After adding the default mime types file, our configuration looks like this:
```bash
events {}

http {
    # include mime.types; (if you did not create a custom file)
    include /opt/homebrew/etc/nginx/mime.types;

    server {
        listen  8080;
        root    /Users/syfe/Desktop/tutorials/nginx-test/html;
    }
}
```

After reloading the conf, our HTML and CSS should be working as expected but now we have support for a lot of types.

#### The `location` block

Let's assume you've the following directory structure:
 
![Directory structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qetn8rftijhco3cscfvx.png)

Going to `/animals`, will serve the `index.html` file inside the `animals` directory. What if you want to have another endpoint `/forest` that serves the same file?

We can use the `location` block to specify the full path and then using `alias` we can set the directory that will serve the files.

```bash
events {}

http {
    include /opt/homebrew/etc/nginx/mime.types;

    server {
        listen  8080;
        root    /Users/syfe/Desktop/tutorials/nginx-test/html;

        location /forest {
            alias /Users/syfe/Desktop/tutorials/nginx-test/html/animals;
        }
    }
}
```

With the above configuration, requests to `/forest` will be served from `animals` directory.

What if someone deletes the `animals` directory or the file we're trying to serve is not available? We can provide fallback files using `try_files` directive.

Let's create a fallback file.

![Directory structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sw0zhj62m0pu195pizpt.png)

Now, let's update the configuration.

```bash
events {}

http {
    include /opt/homebrew/etc/nginx/mime.types;

    server {
        listen  8080;
        root    /Users/syfe/Desktop/tutorials/nginx-test/html;

        location /animals {
            root /Users/syfe/Desktop/tutorials/nginx-test/html;
            try_files /animals/fallback.html /index.html =404;
        }
    }
}
```

Now, when a user visits `/animals`:
1. First the server will look for `/animals/index.html`.
2. If it doesn't exist, it'll look for `/animals/fallback.html`.
3. If our fallback file is missing it'll try to serve the `index.html` at root.
4. If it cannot find any file listed by us, it'll throw a 404 error.

#### Redirects

It is quite easy to set up redirections using NGINX. Let's update the configuration to redirect all incoming traffic from `/birds` to `/animals`.

```bash
events {}

http {
    include /opt/homebrew/etc/nginx/mime.types;

    server {
        listen  8080;
        root    /Users/syfe/Desktop/tutorials/nginx-test/html;

        location /animals {
            root /Users/syfe/Desktop/tutorials/nginx-test/html;
        }

        location /birds {
            return 307 /animals;
        }
    }
}
```

Returning with `307` does the trick.

#### Reverse Proxy

To forward requests to another server we can use the `proxy_pass` directive.

```bash
events {}

http {
    include /opt/homebrew/etc/nginx/mime.types;

    server {
        listen 8080;

        location / {
            proxy_pass http://192.168.10.5:8080;
        }
    }
}
```

Now, all the requests to `localhost:8080` will be forwarded to `http://192.168.10.5:8080`. This is helpful when you don't want to reveal the actual server or want to relay requests to different servers based on different paths.

#### Load Balancing

Sometimes, in high traffic situations, we might spin up multiple instances to handle the load. NGINX can act as a load balancer and evenly distribute requests among all the instances.

![Load balancing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1n38ba394njqdv9xbkv0.png)

This ensures a seamless experience for our users without any downtime.

Let's update the configuration to achieve this:
```bash
events {}

http {
    include /opt/homebrew/etc/nginx/mime.types;

    upstream instances {
        server 192.168.10.2:8080;
        server 192.168.10.3:8080;
        server 192.168.10.4:8080;
    }

    server {
        listen 8080;

        location / {
            proxy_pass http://instances;
        }
    }
}
```

The `upstream` block is used to specify a group of servers that can be referenced by `proxy_pass`. NGINX will automatically take care of distributing the requests evenly.

#### Logging

With all that you've learnt so far, you'll be able to do a lot of cool things. One final(and important) thing that will help you in your journey is - Logging.

Let's update the configuration to add access logs on our server:
```bash
events {}

http {
    include /opt/homebrew/etc/nginx/mime.types;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" ';
    access_log /Users/syfe/Desktop/tutorials/nginx-test/access.log main;

    server {
        listen 8080;
        root /Users/syfe/Desktop/tutorials/nginx-test/html;
    }
}
``` 

We provide the format using `log_format` directive. The `access_log` directive takes the path to store the logs and the format we specified earlier.

---

That's it for this post! I hope this will get you started or at least now you understand what NGINX is. The [official documentation](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/) for NGINX is well written if you wish to explore further.

🤝 I'd love to connect on [LinkedIn](https://linkedin.com/in/thesanjeevsharma) or [X](https://x.com/thesnjvsharma).

[Syfe](https://syfe.com/careers) is hiring devs. 👨‍💻🌏