Node.js server on EC2 and reverse proxy with NGINX

This exercise walks through setting up a node.js server on an EC2 instance and using NGINX as a reverse proxy.

Objective

Set up a Node.js – express server on the AWS EC2 instance and use NGINX for reverse proxy.

Description

In the previous exercise, you set up an EC2 instance on AWS and also installed NGINX. In this exercise, you should set up and start a Node.js – Express server on this EC2 instance. You will also use the NGINX server for reverse proxy.

Scenario:

EC2 instance will be used to deploy a service for the REST API. Since our APIs are developed in Nodejs and express, we first need to install Node.js on our instance. Once Node.js is installed, we can install dependencies or start our server as we would do on our local machine. However, the app would stop running as soon as we terminate our SSH session. So in order to keep our application running alive, we will also need a process manager.

But the application will be running on the URL (<ip address>:<port>). Here <port> is the port number that will be defined in our node app. However, we would want to get rid of the port number. In order to do so, we are going to use port forwarding to achieve this. HTTP uses port 80 by default and we need to forward all the requests made to our server to a port (for example 3000) where our app will be running. We will be using Nginx, which was installed on the EC2 instance in our previous exercise, as a reverse proxy in front of our application server.

A reverse proxy is a server that sits in front of web servers and forwards client (e.g. web browser) requests to those web servers.

Acceptance criteria

  • Install node version manager (nvm) and Node.js on the EC2 instance
  • Create an express.js server
  • Set up a simple express app and create a GET a route such as /menu that sends a JSON object as a response
  • Install pm2 as a task runner
  • Keep the application running alive
  • Use NGINX as a reverse proxy to forward the requests to the port on which the app is running.

 

Hints

  • PM2 is a process manager to keep your application running forever on the server.
  • Install PM2 globally with the following command:
    sudo npm install pm2 -g
    

 

Solution

Introduction

A reverse proxy is a type of server that sits between your clients and the services they are trying to access. Essentially, it acts as an intermediary, providing additional security and control over the services that are being accessed. Unlike a traditional proxy server, which forwards requests from clients directly to other servers, reverse proxies can be used to protect the actual servers themselves. This makes them ideal for use in large networks, such as corporate environments or institutional settings. Additionally, reverse proxies can also help to improve website performance by caching commonly requested resources and reducing reliance on expensive back-end servers. So if you are looking for a way to enhance the security and performance of your network, a reverse proxy may be just what you need.

Reverse proxy vs load balancer

Reverse proxy and load balancer are two similar sounding terms that are often confused. Both reverse proxy and load balancer help improve the performance of a website by distributing traffic across multiple servers. However, there are some key differences between the two. A reverse proxy is typically used to cache static content, such as images, while a load balancer is used to distribute dynamic content, such as user requests. In addition, reverse proxies can provide additional security by hiding the identity of the server from the client, while load balancers do not offer this level of protection. As a result, it is important to understand the difference between reverse proxy and load balancer in order to choose the best solution for your needs.

 

Solution walkthrough

  • Connect to your Ubuntu EC2 instance using EC2 instance connect or through SSH.

  • Node version manager (NVM) will be used to install Node.js on your EC2 instance. Install node version manager (nvm) by typing the following on the command line.

    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
    
  • Activate nvm by typing the following

    . ~/.nvm/nvm.sh
    
  • Install the latest node version. This will also install Node Package Manager (NPM) which will allow you to install additional modules as needed.

    nvm install node
    
  • Test that Node.js is installed and running correctly

    node -v
    

 

  • Next, create an Express app by running the following commands:

    mkdir  express-app
     cd express-app
     npm init -y
     touch server.js
     npm i express
    
  • Open the server.js file

    vim server.js
    
  • And add the following code to it:

    //server.js
    
    const express = require("express");
    const app = express();
    const port = 3000;
    
    app.get("/", (req, res) => {
      res.send({ msg: "Base route" });
    });
    app.get("/menu", (req, res) => {
      res.send({ msg: "Menu items" });
    });
    
    app.listen(port, () => console.log(`Running on port ${port}`));
    

    We can now do node server.js to start the app. However, it will stop running as soon as we terminate our SSH connection. So we will need to install a task runner that will run our app even after closing our SSH session.

  • Install pm2 as a task running to run node apps in the background

    sudo npm install pm2 -g
    



  • Edit security rules: We will need to add the port number that we have set in our app, 3000 in this example so that it can accept requests. To do so, go to the EC2 dashboard, and select the instance. Next, go to the security tab and edit the inbound rules as shown in the following image.

Inbound rules - ec2 instance

 

  • Configure NGINX as a reverse proxy: We are currently running NGINX on port 80 (HTTP uses port 80 by default). And we need to forward all the requests made to our server (port 80) to port 3000 where our app is running. So, we will be using NGINX as a reverse proxy in front of our application server. NGINX will help to intercept the request and forward them to port 3000. To do this, open the default Nginx configuration file in the sites-enabled folder of Nginx using the following command

    cd /etc/nginx/sites-enabled
    

    If you list files in this folder using the ls command, it will show a default file. Open this default file.

    vim default
    

    Then delete all the contents of this file and copy this snippet. This will allow forwarding all requests made to the base path / to port 3000 where our app is running.

    server {
     listen         80 default_server;
     listen         [::]:80 default_server;
     server_name    localhost;
     root           /usr/share/nginx/html;
     location / {
         proxy_pass http://127.0.0.1:3000;
         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;
      }
    }
    
    

    Save the configuration and restart Nginx with the following command.

    sudo service nginx restart
    

 

  • Start the application with PM2 Go back to the root directory

    cd ~/express-app
    

    And start the express server with pm2

    pm2 start server.js
    

Server running:

server running ec2 instance

 

  • Test the route: Get the IP address from the EC2 dashboard and visit it from your browser. Example: http://18.216.118.130/. It should respond with the following message: { msg: "Base route" }

GET response:

get response - with nginx reverse proxy