How to Install NGINX on Ubuntu: Reverse Proxy Setup with HTTP/2, TLS, and Rate Limits

Setting up NGINX on Ubuntu is quite easy, but configuring it to function as a powerful reverse proxy may become challenging without a definitive tutorial. If configured properly, NGINX can easily route traffic to backend apps, distribute requests across multiple servers, and protect your services with rate limitations.
Running NGINX effectively also depends on the infrastructure behind it. ServerMania’s dedicated Ubuntu hosting solutions offer the performance, reliability, and flexibility required for powerful reverse proxies, load-balanced environments, and high-traffic apps, giving admins full control over server configurations.
This guide is meant for system administrators, developers, and Linux enthusiasts who want to learn how to install NGINX on Ubuntu, set it up as a reverse proxy, and enable TLS and HTTP/2.
What Is NGINX and Why Use It as a Reverse Proxy?
NGINX is a reliable web server that can work as a proxy server between the client and backend servers. Instead of forwarding client requests straightforwardly to applications, NGINX reverse proxy acts like a proxy layer that handles all of the incoming requests and forwards traffic to the appropriate destination.
The primary idea behind using NGINX as a reverse proxy is to improve the overall security of the server, simplify the SSL termination, and support features like load balancing across multiple backend servers. In addition, it also helps distribute the total load, improve high availability, and optimize how applications handle requests and return responses.
Requirements:
Before you get started, verify the server is equipped with:
- Nginx is compatible with Ubuntu 22.04 and 24.04 LTS.
- Ubuntu non-root user account with sudo rights enabled
- At least one running application to receive the requests
- The application IP address or domain name of your app
- An active internet connection for the installation purpose
Note: This guide assumes you have a basic understanding of Linux, networking concepts such as port and IP address assignments, and access to a terminal.
How to Install NGINX on Ubuntu as a Reverse Proxy
The setup consists of several steps, from installing NGINX and setting up as a reverse proxy to learning how to save client data with proxy headers and configuring TLS and HTTP/2. In addition, we’ll learn how to establish WebSocket support and configure rate limiting.
Step 1: Installation of NGINX on Ubuntu
Installing NGINX on Ubuntu is a straightforward process using the default APT package manager. NGINX installation commands are consistent across Ubuntu LTS versions. However, first update your Ubuntu index package to ensure you’re installing the latest NGINX version:
sudo apt updateYou must allow traffic through the firewall for NGINX access. The UFW firewall needs to allow incoming web traffic for NGINX to function correctly. NGINX registers itself with the UFW firewall upon installation. Nginx offers three application profiles for firewall configuration: NGINX Full, NGINX HTTP, and NGINX HTTPS. So, you must check the profiles and allow them through:
sudo ufw app list
sudo ufw allow 'Nginx HTTP'
sudo ufw allow 'Nginx Full'Nginx is installed using ‘sudo apt install nginx’:
sudo apt install nginx -yNginx can be verified as running with ‘systemctl status nginx‘:
sudo systemctl status nginxIn case NGINX is not active (not running), you should start the service and configure it to launch even after a server reboot. NGINX can be controlled using systemctl commands to start, stop, restart, or reload the service. Run both of these commands sequentially:
sudo systemctl start nginx
sudo systemctl enable nginxNow, open a browser and insert your server’s IP address. For example, “https://http://203.0.113.10” and you should be able to see the default NGINX welcoming page. This confirms that your server has been successfully configured and is ready for the reverse proxy setup.
Note: NGINX configurations can be tested for syntax errors using ‘sudo nginx -t’.
Step 2: Understanding the Configuration
To be able to set up NGINX as a reverse proxy thoughtfully, it’s best to understand how the configuration file is structured. The main configuration file is located at “/etc/nginx/nginx.conf“. The settings of other apps and the individual settings can be found at “/etc/nginx/sites-available/“.
The most critical component that you’ll be using is the server block, which determines exactly how the server will handle all the incoming requests. This is based on the host, port, and the requested location. Each directive in the config file manages a specific behaviour, allowing you to manage different websites.
A default directory structure looks like this:
/etc/nginx/
├── nginx.conf
├── sites-available/
├── sites-enabled/
├── conf.d/
└── snippets/Understanding where these files are stored makes future configuration changes easier, especially when you need to update settings, troubleshoot an error, or modify a recently changed reverse proxy setup.
Note: Default web files for Nginx are located in ‘/var/www/html’.
Step 3: Set Up NGINX as a Reverse Proxy
When NGINX is installed, you can now configure NGINX to forward traffic to any application. In our example, we’ll use port 3000 on the same server and have NGINX receive incoming requests and forward them to the application we have installed (Node.js Express).
Open the correct configuration file in the sites-available directory:
sudo nano /etc/nginx/sites-available/reverse-proxyAdd the following basic configuration:
server { listen 80;
servername yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}In this server block, the location/directive tells NGINX to forward all HTTP requests to the app listening on port 3000, in our case, Node.js Express. The proxy_pass HTTP setting defines where proxy requests should be sent, allowing NGINX to function as a proxy layer between the client and the backend server.
Save the file, then enable the site by creating a symbolic link:
sudo ln -s /etc/nginx/sites-available/reverse-proxy /etc/nginx/sites-enabled/Before applying the changes, test the configuration:
sudo nginx -tIf the test is successful, reload NGINX:
sudo systemctl reload nginxFinally, open your domain or server IP address in the browser. You can now see NGINX receiving all the client requests and forwarding them to the selected application. DNS issues may prevent your domain from resolving correctly. So, from the visitor’s perspective, the app appears to be served directly by NGINX, even though the requests are being processed by a service running on port 3000.
However, NGINX logs can help identify upstream server issues. For example, A 502 Bad Gateway error indicates backend server issues. Also, SSL misconfiguration can cause connection failures with NGINX.
Note: Always check if the backend server is listening on the correct port.
Step 4: Save Client Data via Proxy Headers
If you leave the NGINX configuration by default, it will forward client requests to a backend server, and the application will only see the IP address of the proxy instead of the original visitor. This would become an issue if the app relies on the actual IP for access controls, rate limiting, logging purposes, or security.
So, passing the correct headers preserves information about the original request and helps the backend identify the expected address of the client.
To be able to retain this data, update the location block in your reverse proxy configuration to include the following proxy_set_header x directives:
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Here’s how this works in practise:
| Header: | Purpose: |
|---|---|
| Host | Preserves the original host information requested by the client |
| X-Real-IP | Passes the visitor’s x real IP, which corresponds to the real IP remote_addr. |
| X-Forwarded-For | Maintains a list of IP address values if the request passes through multiple proxies. |
| X-Forwarded-Proto | Tells the application whether the original connection used HTTP or HTTPS. |
After updating the config, test the configuration and reload NGINX:
sudo nginx -t sudo
systemctl reload nginxWith the headers set up, the backend server will receive the actual client information while NGINX will continue handling all the proxy requests.
Step 5: Upstream Blocks & Load Balancing
So, in case the application traffic outgrows a single backend server, NGINX supports load balancing for multiple backend servers. This provides high availability, load distribution, and reduces the risk of failure. To achieve this, define an upstream group containing your backend servers, then direct proxy requests to that group instead of a single destination:
upstream app_servers {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://app_servers;
}
}We now have multiple upstream blocks that allow NGINX to distribute all incoming requests across more than one application instance. NGINX supports several load balancing algorithms, including Round Robin. By default, NGINX will use the round robin method, meaning it will send subsequent requests to each server in sequence.
You can also implement advanced load balancing techniques such as least connections or IP hash for session persistence, depending on your application’s requirements.
Note: Nginx can handle session persistence with sticky sessions.
Step 6: Enable NGINX WebSocket Support
NGINX can handle WebSocket connections for real-time communication.
In contrast to the ordinary HTTP traffic, WebSocket connections can remain open and allow data to be exchanged between the client and the backend server continuously. This is what makes the connections ideal for everything from chat apps, dashboards, gaming platforms, and real-time notification systems.
NGINX buffers responses from proxied servers by default.
To support WebSockets and disable buffering for these long-lived connections, you need to update your location block as follows:
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_buffering off;
}In this example, the “Upgrade” and connection header directives instruct NGINX to change from the ordinary HTTP connection to a WebSocket connection. Therefore, disabling the buffer will guarantee that returning responses are sent immediately, instead of being stored by the proxy.
Step 7: Enable the NGINX TLS and HTTP/2
Implementing SSL will encrypt the traffic between the server and the client, which is the ultimate way to secure any sensitive data reaching the app. HTTP/2 will further enhance the performance by allowing multiple requests to be managed through a single connection.
Here’s how to update your server block to listen for HTTPS traffic and enable HTTP/2:
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
}
}If you do not already have an SSL certificate, Certbot automates SSL certificate installation and renewal. It integrates with NGINX and configures HTTPS with minimal manual intervention. You can also use the “LetsEncryt” Let’s Encrypt certificates, which are valid for 90 days. Certbot automatically renews them before expiration, helping maintain continuous SSL protection without additional administrative effort.
This configuration will allow NGINX to perform SSL termination, which will handle encrypted traffic prior to forwarding proxy requests. Don’t forget to restart NGINX when ready.
See Also: How to Set Up PHP on Nginx with FastCGI PHP-FPM Configuration
Step 8: Rate Limiting and Request Controls
A rate limitation will help you protect the server by specifying exactly how many requests a client can make within a predefined time period. In addition, you can also apply special handling to a specific location or specified URI, allowing stricter limits for login pages, APIs, or other sensitive endpoints.
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
listen 80;
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
client_max_body_size 20M;
proxy_pass http://127.0.0.1:3000;
}
}In this example, we used NGINX built-in variables to check each client and limit their traffic accordingly. The client_max_body_size directive determines the maximum size of incoming uploads, while the empty string can be used paired with advanced conditions to evaluate request values. These controls will apply regardless of whether different protocols are used by the app/proxy.
Note: Since each worker process enforces these rules efficiently, NGINX maintains performance while improving security and reducing unnecessary load on your application servers
Step 9: Test and Validate the Configuration
When you’re ready, before putting the reverse proxy into production, verify that everything is working:
sudo nginx -tIf the test succeeds, reload the service to apply any recently changed settings:
sudo systemctl reload nginxFinally, open your domain in a browser to confirm that NGINX is forwarding requests correctly and that no error messages appear.
See Also: NGINX vs. Apache: Which Is the Best Server for You?
Deploy Ubuntu NGINX with ServerMania

Deploying NGINX on Ubuntu requires an underlying infrastructure that delivers performance across one or multiple backend servers as your workflow grows.
ServerMania Dedicated Servers provide the flexibility and resources you need to support NGINX reverse proxies, load-balanced environments, and demanding projects. With enterprise-grade hardware, global data center locations, and full administrative control, we give you the foundation to build, secure, scale, and manage your Ubuntu deployments with confidence.
💬If you have questions, contact our 24/7 customer support or book a free consultation with an NGINX expert to discuss your next Ubuntu deployment. We’re available right now!
Was this page helpful?
