Understanding Nginx: From Simple Concepts to Technical Details and Deploying securely with PM2 and Certbot
Tags: node, webdev, javascript, tutorial
Published: 2024-05-02 17:03:23

Imagine you have a little shop where you sell lemonade, and you have a big sign that says "Lemonade for Sale." When people see your sign, they come to your shop to buy your lemonade. Now, imagine you're so popular that lots of people come at the same time. You need to make sure everyone gets their lemonade quickly and smoothly, so no one is waiting too long.

Nginx is like the best helper you could ask for in your shop. It's like a smart organizer who helps you handle all the people coming to your shop. When someone comes up to buy lemonade, Nginx quickly figures out who should get lemonade first, and makes sure that even if lots of people come at once, everyone gets their drink fast and without any trouble. It helps you manage the crowd so you can keep everyone happy!

### WHAT??

Certainly! Nginx (pronounced as "Engine-X") is a powerful web server software that is also used as a reverse proxy, load balancer, and HTTP cache. It was specifically designed to handle high concurrency, which means it can manage many connections at once, efficiently and without requiring a lot of resources.

**Web Server:** As a web server, Nginx serves static content, like HTML, CSS, and JavaScript files, directly to clients. It is very efficient at this because of its event-driven architecture, which allows it to handle thousands of simultaneous connections on a single thread without much memory overhead.

**Reverse Proxy:** As a reverse proxy, Nginx sits between the client (like a web browser) and other servers. It can forward client requests to other servers and deliver the responses back to the client. This setup is useful for hiding the characteristics and location of the origin servers or for load balancing.

**Load Balancer:** Nginx can distribute incoming network traffic across multiple backend servers (like a group of application servers). This distributes the load, making sure no single server gets overwhelmed, which improves the responsiveness and availability of applications.

**HTTP Cache:** Nginx can also cache the responses from a server and reuse them to answer identical requests. This reduces the load on the server and speeds up the response time for clients.

Nginx is known for its high performance, stability, rich feature set, simple configuration, and low resource consumption. These qualities make it a popular choice for scaling web applications and improving their performance and reliability.

**Lets Use NGINX to run the Sigle File NodeJS Server that we set up [here](https://eneaslari.com/articleview/render/hosting-a-nodejs-web-app-on-ubuntu-server-22.04-6628fdeac48df087e19c296b "Hosting a NodeJS Web App on Ubuntu Server 22.04")**

Nginx, pronounced like “engine-ex”, is an open-source web server that, since its initial success as a web server, is now also used as a reverse proxy, HTTP cache, and load balancer.

**Installing Nginx**

```bash
apt install nginx
```

**UFW**

Uncomplicated Firewall (UFW) is a program for managing a netfilter firewall designed to be easy to use. It uses a command-line interface consisting of a small number of simple commands, and uses iptables for configuration. UFW is available by default in all Ubuntu installations since 8.04 LTS.[1] UFW has been available by default in all Debian installations since 10.

Install ufw

```bash
apt install ufw
ufw enable
```

Give nginx full control of firewall

```bash
ufw allow "Nginx Full"
```

When you install Nginx, a default configuration file is created at `/etc/nginx/sites-available/default`. It's a good idea to keep this file for reference, but if you don't want to use it immediately, you should disable it. To disable it, delete the symbolic link in `/etc/nginx/sites-enabled/` that points to this default file.

Now let's Create a new config file with name `seinglefileserver.conf ` under `/etc/nginx/sites-available/` directory

```bash
server {
  server_name test.eneaslari.com;
  location / {
    proxy_pass http://<Your servers Ip>:6001;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    }
}
```

This Nginx configuration file defines a server block that listens for requests directed to the domain `test.eneaslari.com`. Let's break down each directive:

1. `server`: Begins a new server block.

2. `server_name test.eneaslari.com`: Specifies the domain name for which this server block will be used to handle requests. In this case, it's set to `test.eneaslari.com`.

3. `location /`: Defines a location block that matches requests with any URI path.

4. `proxy_pass http://<Your servers Ip>:6001;`: This directive instructs Nginx to forward incoming requests to another server. In this case, it forwards requests to the IP address specified (replace `<Your servers Ip>` with the actual IP address) on port 6001.

5. `proxy_http_version 1.1;`: Sets the HTTP version used in proxy communication to HTTP/1.1.

6. `proxy_set_header Upgrade $http_upgrade;`: This sets the `Upgrade` header to the value of the `Upgrade` header from the client's request. This is often used to enable WebSocket connections.

7. `proxy_set_header Connection 'upgrade';`: Sets the `Connection` header to `upgrade`. This is also commonly used for WebSocket connections.

8. `proxy_set_header Host $host;`: Sets the `Host` header to the value of the `Host` header from the client's request.

9. `proxy_cache_bypass $http_upgrade;`: Sets conditions under which the request will bypass the cache. In this case, requests with the `Upgrade` header will bypass the cache.

This configuration essentially sets up a reverse proxy that forwards incoming requests from `test.eneaslari.com` to another server listening on port 6001. It also includes settings commonly used for WebSocket connections.

Here's a clearer breakdown of how Nginx's configuration files are structured:

1. **Configuration Storage:**
   - **Available Configurations:** All the server block files are stored in `/etc/nginx/sites-available/`.
   - **Enabled Configurations:** Nginx will only use configurations that are linked in `/etc/nginx/sites-enabled/`. These are usually symbolic links to files in the `sites-available` directory.

2. **Enabling a New Configuration:**
   - To activate a configuration file, create a symbolic link from the `sites-available` directory to the `sites-enabled` directory using the following command:
     ```
     ln -s /etc/nginx/sites-available/<filename> /etc/nginx/sites-enabled/<filename>
     ```
   - Replace `<filename>` with the name of the configuration file you wish to enable.

If you want to unlink the default Nginx configuration, you can use the following command:

```bash
sudo unlink /etc/nginx/sites-available/default
```

**Check Nginx Configuration**: Before restarting Nginx, it's a good practice to check if the configuration is valid.

   ```bash
    sudo nginx -t
   ```

   If there are any syntax errors in your configuration file, this command will point them out. Fix any errors before proceeding.

**Restart Nginx**: Once the configuration is validated, you can restart Nginx to apply the changes.

  ```bash
    sudo systemctl restart nginx
  ```

   This command will restart Nginx and load the new configuration file. From this point onwards, Nginx will handle requests for `test.eneaslari.com` according to the configuration you provided.

**Test the Configuration**: After restarting Nginx, you can test whether it's working correctly by accessing `http://test.eneaslari.com` in your web browser or by sending requests to this domain using tools like `curl`.

Be sure that you are running the nodeJS Server for example `npm start`

That's it! Your Nginx server should now be configured to handle requests for `test.eneaslari.com` and proxy them to the specified server.

Now Visit test.eneaslari.com/ (for you it will be something different depends on what you have defined your servers name. Also you have to be sure that you have connected the specified domain from your domain provider with the server)

### Optional

Now let's take it to the next level and use PM2 to run server in the background

Create at the root of your project a file with name ecosystem.config.cjs

Past this code :

```bash
module. Exports = {
  apps: [{
    name: "test.eneaslari.com",
    script: "npm start",
    watch: ["./"],
    ignore_watch: ["node_modules"],
  }]
}
//run this file with pm2 with pm2 start ecosystem.config.cjs
```

- `module.exports`: This syntax is used in Node.js to export modules, making them available to other files. In this case, it's exporting an object that contains PM2 configuration.

- `apps`: This key holds an array of objects, where each object represents a configuration for a specific application or microservice that PM2 will manage. You can include multiple configurations in the `apps` array for different applications.

- `name`: A string that specifies the name of the application as it will appear in PM2's process listing. In this case, the application is named "test.eneaslari.com".

- `script`: Defines the entry point script for the application. Here, `"npm start"` is used, which means PM2 will execute the `start` script defined in your `package.json` file.

- `watch`: If set to `true`, PM2 will automatically restart the application when file changes in the current directory are detected. Here, it's set to watch all changes in the directory where PM2 is started (`"./"`). This is useful during development for automatic restarts on file save.

- `ignore_watch`: An array of patterns to ignore from watching. In this configuration, changes in the `node_modules` directory won't trigger a restart. This is important because `node_modules` can contain a large number of files and frequent changes, which aren't typically necessary to watch for development purposes.

1. **Install PM2**: If you haven't already installed PM2, you can do so globally using npm:

    ```bash
    npm install -g pm2
    ```


2. **Navigate to Your Project Directory**: Open your terminal or command prompt and navigate to your project directory where the `ecosystem.config.js` file is located.

3. **Start Your Application with PM2**: Run the following command to start your application using PM2:

    ```bash
    pm2 start ecosystem.config.cjs
    ```
![alt text](https://eneaslari.com/9934e34c-36fc-4617-b91c-add0bc886bdd.PNG "How nodejs work under the hood")


   This command will start your application as defined in the PM2 configuration file (`ecosystem.config.js`). PM2 will manage your application process and keep it running in the background.

5. **Check Application Status**: You can check the status of your application by running:

    ```bash
    pm2 status
    ```

   This command will show you a list of applications managed by PM2 along with their status.

6. **Monitor Application Logs**: To monitor application logs, you can use the following command:

    ```bash
    pm2 logs
    ```

More pm2 commands [here ](https://pm2.keymetrics.io/docs/usage/quick-start/ "nameforurl")

**Securing Your Node.js Application with SSL**

Ensuring that your Node.js application is secure is crucial, especially when dealing with sensitive data. One effective way to secure your application is by implementing SSL/TLS, which encrypts the data transmitted between your server and clients. Below is a step-by-step guide on how to set up SSL for a Node.js application hosted with Nginx using Certbot and Let's Encrypt.

***Step 1: Install Certbot and Its Nginx Plugin***

First, you need to install Certbot and the Certbot Nginx plugin on your server. Certbot is a free, automated tool that simplifies the process of obtaining and renewing Let’s Encrypt SSL certificates. To install these packages, run:

```bash
sudo apt install certbot python3-certbot-nginx
```

This command installs Certbot along with the Nginx plugin, which allows Certbot to automatically configure SSL for Nginx.

***Step 2: Obtain an SSL Certificate***

Once the installation is complete, you can proceed to obtain an SSL certificate for your domain. Replace `test.eneaslari.com` with your actual domain name:

```bash
sudo certbot --nginx -d test.eneaslari.com
```

This command instructs Certbot to authenticate your domain, obtain an SSL certificate, and configure Nginx to use this certificate. Follow the interactive prompts to complete the process. Ensure that your domain is pointed to the server where you are running this command and that port 80 is open, as Certbot uses it to validate domain ownership.

***Step 3: Verify Automatic Renewal Setup***

Let’s Encrypt certificates are valid for ninety days. Fortunately, Certbot creates a scheduled task to automatically renew certificates before they expire. Check the status of the Certbot timer to confirm it's active:

```bash
sudo systemctl status certbot.timer
```

This command will show you whether the timer is active and when the next renewal attempt is scheduled. It's important that this service is running to avoid any interruptions due to an expired certificate.

***Final Step: Confirmation***

Congratulations! You have successfully secured your Node.js application with SSL/TLS using Certbot and Let's Encrypt. Your communications are now encrypted, significantly enhancing the security of your application.