Ikeh Akinyemi Ikeh Akinyemi is a software engineer based in Rivers State, Nigeria. He’s passionate about learning pure and applied mathematics concepts, open source, and software engineering.

How to use Nginx as a reverse proxy for a Node.js server

6 min read 1728

Nginx Logo

Node.js is a vital tool for almost all kinds of microservices-based development and delivery. It’s also the leading tool for creating server applications in JavaScript and offering the functionality of both a web server and an application server.

But Node.js has some shortcomings and vulnerabilities that can cause unsatisfactory performance or even crashes within Node-based applications. For example, Node.js-based web applications are prone to slow code execution or even crashes due to IO-bound operations or rapid traffic growth. They also sometimes struggle with serving static content such as images and JavaScript files and load balancing across multiple servers.

Fortunately, you can cache static content, reverse proxy and load balance among multiple application servers, and manage port contention between clients using Nginx. This makes Nginx an excellent tool for increasing Node.js performance.

In this tutorial, we’ll show you how to reverse proxy a Node.js application with Nginx. We’ll build a simple Node.js application that will run on port 3000 and use Nginx as a reverse proxy server for the Node.js application. The application will be addressed using a domain name.

Using Nginx As Reverse-Proxy
Source: Kinsta

To follow along, you should have:

  • An understanding of how the web, web servers, and web browsers communicate
  • Basic knowledge of JavaScript, Node.js, asynchronous programming, computer networking, and DNS
  • An Ubuntu virtual machine from a public cloud provider, open to ports 22, 80, and 3000
  • Node.js installed on your local machine
  • A domain name from a DNS registrar
  • Vim text editor installed on your local device

What are servers and web servers?

A server is a computer that communicates with other computers to serve them with the information requested by these computers. These computers, also called clients, connect to a server through either a local area network (LAN) or a wide area network (WAN). A server sends and collects information across a network within multiple locations.

The server on the web, known as a web server, through the Hypertext Transfer Protocol (HTTP), receives requests from a client via the internet, such as the browser, and returns an HTTP response, either as an HTML webpage or in a JSON format as with APIs calls.

Web servers are the foundation of any data exchange using HTTP as its client-server protocol for communication with other computers. A web server as a computer involves both hardware and software and is a fundamental component in web development. The software side makes up the parts that understand URLs and control how web users access hosted files.

What is Nginx?

According to the documentation, Nginx (pronounced “engine X”) is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev.

Nginx is used for a variety of tasks that contribute to improving Node.js performance. Key features include:

  • Reverse proxy server — As traffic to your app increases, the best approach to improve performance is to use Nginx as a reverse proxy server in front of the Node.js server to load balance traffic across the servers. This is the core use case of Nginx in Node.js applications
  • Stateless load balancing — This improves performance while reducing load on backend services by sending off client requests to be fulfilled by any server with access to the requested file
  • Cache static contents — Serving static content in a Node.js application and using Nginx as a reverse proxy server doubles the application performance to a maximum of 1,600 requests per second
  • Implement SSL/TLS and HTTP/2 — Given the recent shift from using SSL/TLS to secure user interactions in Node.js applications, Nginx also supports HTTP/2 connections
  • Performance tracking — You can keep real-time tabs on the overall performance of your Node.js application using the stats provided on Nginx’s live dashboards
  • Scalability — Depending on what assets you’re serving, you can take advantage of the full‑featured HTTP, TCP, and UDP load balancing in Nginx to scale up your Node.js application

Nginx currently supports seven scripting languages: Go, Node.js, Perl, PHP, Python, Ruby, and Java Servlet Containers (the last is an experimental module). It enables you to run applications written in different languages on the same server.

Let’s set up our Node.js application.

Creating a Node.js application

For this simple Node.js application, we’ll build a Node.js server with the HTTP module provided by Node.js. Let’s started by creating a folder and initializing the project on the terminal:

mkdir nginx_server_project
cd nginx_server_project
npm init -y

The above code will create the folder nginx_server_project and change the directory into the folder. We then initialize a Node.js application with npm, using the -y flag to set yes as the default answer to all the questions.

The next step is to create the server.js file that contains the source code for our application. Open it up with any IDE or text editor of your choice:

touch server.js
vim server.js

Now it’s time to build and start the server. Let’s define two extra subdomains as a means to test that our application is fully functional:

const http = require("http");

const server = http.createServer((req, res) => {
  const urlPath = req.url;
  if (urlPath === "/overview") {
    res.end('Welcome to the "overview page" of the nginX project');
  } else if (urlPath === "/api") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(
      JSON.stringify({
        product_id: "xyz12u3",
        product_name: "NginX injector",
      })
    );
  } else {
    res.end("Successfully started a server");
  }
});

server.listen(3000, "localhost", () => {
  console.log("Listening for request");
});

We created a server with a Node.js HTTP module that we imported using the require function in the above code. Within our server, we’ll render two different responses, depending on our current route. The two routes are /overview and /api.

On the /overview subdomain, we’ll render a plain text, while on the /api, we’ll render a JSON object. The above application will be accessed on the Public IPv4 address of your virtual machine — e.g., 34.211.115.4 on port 3000.



Now that the Node server application is ready, let’s install Nginx and configure it.

Installing Nginx

We’ll install Nginx using the default package manager for a Debian-based operating system, which is called apt. Nginx is also available for almost all operating systems under their default repositories.

Before installing Nginx, make sure you’ve installed the prerequisites for Ubuntu OS.

Next, we’ll configure Nginx based on the unique needs of our project, then we’ll be ready to implement it.

Configuring Nginx

For Nginx to route to the Node.js application listening on port 3000, we’ll need to first unlink the default configuration of Nginx and then create a new configuration to be used for by our Node.js application.

To unlink the default Nginx configuration, you can use the following command:

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

The Nginx configuration is kept in the /etc/nginx/sites-available directory. To create a new configuration, let’s navigate to this directory and create a configuration file pointing to the server block of our Node.js application.

cd /etc/nginx/sites-available
touch myserver.config

After changing the directory to /etc/nginx/sites-available, the second command will create an Nginx configuration file named myserver.config.

Next, open the myserver.config file:

sudo vim /etc/nginx/sites-available/myserver.config

Paste in the following configuration:

#The Nginx server instance
server{
    listen 80;
    server_name wach.quest;
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        # location /overview {
        #     proxy_pass http://127.0.0.1:3000$request_uri;
        #     proxy_redirect off;
        # }
    }
}

The above configuration has Nginx listening on port 80 on your-domain.com. The / is your Uniform Resource Identifier (URI) with the following properties:

  • proxy_set_header, which sets the host header to be that of the Nginx server
  • proxy_pass http, which instructs Nginx to proxy all requests matching the location pattern to an upstream (backend) server
  • proxy_http_version, which converts the incoming connection to HTTP 1.1
  • proxy_set_header Upgrade, which converts the proxied connection to type Upgrade because WebSockets only communicate on upgraded connections
  • proxy_set_header Connection, which ensures the connection header value is U``pgrade

Save the changes and exit the file by clicking the Esc key. Then, type the command :wq and hit the Enter or return key.

For the next step, let’s enable the above file by creating a symbolic from it to the sites-enabled directory, which Nginx reads from during startup:

sudo ln -s /etc/nginx/sites-available/myserver.config /etc/nginx/sites-enabled/

The server block is now enabled and configured to return responses to requests based on the listen port and location path.

Now it’s time to start both our Node.js application and the Nginx service to trigger the recent changes. But first, let’s check the status of Nginx to confirm that the configuration is working properly:

sudo nginx -t 

The output upon running the above command would look like this:

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

The above output confirms that our configuration was successful. Next, restart Nginx to enable your changes:

sudo systemctl restart nginx

With Nginx running again, let’s allow full access through the Nginx firewall:

sudo ufw allow 'Nginx Full'

Next, navigate to the directory of the Node.js application:

cd ~/nginx_server_project

Start your Node.js server application using the following command:

node server.js

Node.js Server Running And Listening For A Request

Open your browser and access the Node.js application using your-domain.com:

Accessing The Node.js Application

Now we can navigate to the address your-domain.com/overview on the browser and access the /overview endpoint of our application:

Accessing The /overview Endpoint

To further test if every other path we defined is working, let’s try the last path, your-domain.com/api:

Accessing The /api Endpointq

Conclusion

In this tutorial, we learned how to set up Nginx as a Node.js server for backend applications.

We built a simple Node.js application using Node.js and Nginx. Then, we configured Nginx to listen for port 3000 and serve the contents we have predefined within our Node.js application on the browser.

Ngnix is a great tool for web application delivery, providing performance and scalability at both the ADC and web server layers.

200’s only Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Ikeh Akinyemi Ikeh Akinyemi is a software engineer based in Rivers State, Nigeria. He’s passionate about learning pure and applied mathematics concepts, open source, and software engineering.

6 Replies to “How to use Nginx as a reverse proxy for…”

  1. Nice one bro. Simple and concise explanation. Keep it up. Thank you. I benefited a lot from this article.

  2. Hello Mukhtar,

    I’m really happy you found this helpful. Thanks you for leaving a comment!

  3. On a grade scale this gets an F only because there is nothing lower. That you copy-pasted a rule for each path is lame to say the least. What was improved by inserting nginx in front of node in this example? Answer: nothing but additional overhead. You said “For Nginx to listen for port 3000 from our Node.js application”… but in no way shape or form does nginx “listen” to node. It is listening to port 80 and forwarding requests to node on port 3000. Then out of the blue you say “For the default port 127.0.0.1 to work…” First, that’s not a port it’s an IP address, but you already had a rule for / to proxy to 3000. Was that not working? Why is it there? You then say “We’ll use the port 127.0.0.1:8080”. Again, that’s not a port but at least it has a port this time. Finally, you gratuitously threw in the extra “listen [::]:8080 default_server” statement without explanation. You turned what should have been a dirt simple how-to into an incoherent mess full of misstatements and errors of omission.

    1. Hello Rick,

      I have updated the article, fixing issues raised as well as taking you and other readers on a journey on how better to serve NodeJs applications through Nginx.

      Please do leave a comment or feedback and I will duly attend to them.

      1. Thanks for the article! I followed you instructions and it got me set-up exactly the way I wanted.

  4. Hi,

    Thanks for these settings. When I used these with my docker node-app, and it only worked on the main page (www.example.com), but whenever I clicked on http://www.example.com/contactus, the links on my site kept taking me to 127.0.0.1:3000, which is wrong.

    Can you please help?

    Thanks

Leave a Reply