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.

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 serverproxy_pass http
, which instructs Nginx to proxy all requests matching the location pattern to an upstream (backend) serverproxy_http_version
, which converts the incoming connection to HTTP 1.1proxy_set_header Upgrade
, which converts the proxied connection to typeUpgrade
because WebSockets only communicate on upgraded connectionsproxy_set_header Connection
, which ensures the connection header value isU``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
Open your browser and access the Node.js application using your-domain.com
:
Now we can navigate to the address your-domain.com/overview
on the browser and access the /overview
endpoint of our application:
To further test if every other path we defined is working, let’s try the last path, your-domain.com/api
:
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. 
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. Start monitoring for free.
Nice one bro. Simple and concise explanation. Keep it up. Thank you. I benefited a lot from this article.
Hello Mukhtar,
I’m really happy you found this helpful. Thanks you for leaving a comment!
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.
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.
Thanks for the article! I followed you instructions and it got me set-up exactly the way I wanted.
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