Set Up an Nginx Reverse Proxy on Ubuntu
Configure Nginx as a reverse proxy on Ubuntu 24.04: proxy_pass to a backend app, forward the right headers, and add auto-renewing HTTPS with Let's Encrypt.
A reverse proxy sits in front of one or more backend applications and forwards client requests to them. Nginx is the most popular tool for the job: it terminates TLS, routes by hostname or path, adds caching and compression, and shields an app server that was never meant to face the public internet directly. A typical use case is running a Node.js, Python, or Java app on localhost:3000 and exposing it on ports 80/443 under a real domain.
This tutorial sets up Nginx as a reverse proxy on Ubuntu 24.04 LTS: install Nginx, write a server block that proxies to a backend, forward the correct headers, then add free HTTPS with Letโs Encrypt. Every command and config snippet is production-shaped and copy-pasteable.
Prerequisites
- Ubuntu 24.04 LTS server with a user that has
sudo. - A domain name (e.g.
app.example.com) with an A record pointing at the serverโs public IP โ required for the HTTPS step. - A backend application already listening locally, for example on
127.0.0.1:3000. - Ports 80 and 443 reachable. If you run a firewall, see our UFW firewall guide.
Step 1: Install Nginx
Update the package index and install Nginx from the Ubuntu repository:
sudo apt update
sudo apt install -y nginxNginx starts automatically. Confirm it is running and enabled at boot:
systemctl status nginxBrowse to http://your-server-ip and you should see the default โWelcome to nginxโ page.
Step 2: Allow HTTP and HTTPS through the firewall
If UFW is active, open the web ports using the bundled application profile:
sudo ufw allow 'Nginx Full'
sudo ufw statusNginx Full opens both 80 and 443. Use Nginx HTTP if you only need port 80 for now.
Step 3: Create a reverse proxy server block
Ubuntu organises site configs under /etc/nginx/sites-available/ and enables them via symlinks in sites-enabled/. Create a config for your domain:
sudo nano /etc/nginx/sites-available/app.example.comPaste the following, adjusting the domain and backend address:
server { listen 80; listen [::]:80; server_name app.example.com;location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; 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; # WebSocket support proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
}
The proxy_set_header lines matter: without Host and X-Forwarded-*, backend apps see the wrong hostname and think every request comes from 127.0.0.1, breaking redirects, logging, and rate limiting.
Step 4: Enable the site and reload
Enable the config by symlinking it into sites-enabled/, test the syntax, then reload:
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxThe nginx -t output should end with syntax is ok and test is successful. Always run it before reloading โ a bad config will refuse to reload rather than take down the running server.
Optionally remove the default site so it does not catch unmatched hostnames:
sudo rm /etc/nginx/sites-enabled/default
sudo systemctl reload nginxStep 5: Verify the proxy works
Request your site through Nginx and confirm the backend responds:
curl -I http://app.example.comYou should get a 200 OK (or whatever your app returns) with a Server: nginx header. If you see 502 Bad Gateway, Nginx reached no backend โ verify the app is actually listening on the address in proxy_pass with ss -tlnp | grep 3000.
Step 6: Add HTTPS with Letโs Encrypt
Install Certbot and its Nginx plugin via snap, the method the project recommends on Ubuntu:
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotRequest and install a certificate. Certbot edits your server block to add the TLS configuration and an HTTPโHTTPS redirect automatically:
sudo certbot --nginx -d app.example.comAnswer the prompts (email, terms). Certbot installs a systemd timer that renews certificates before they expire. Confirm renewal works with a dry run:
sudo certbot renew --dry-runStep 7: Confirm the secure setup
Test the HTTPS endpoint and the redirect from plain HTTP:
curl -I https://app.example.com
curl -I http://app.example.comThe HTTPS request should return 200, and the HTTP request should return a 301 redirect to the https:// URL.
Troubleshooting and common pitfalls
- 502 Bad Gateway โ the backend is down or
proxy_passpoints to the wrong host/port. Check withss -tlnpand the app logs. - Redirect loops โ the backend forces HTTPS itself but does not trust
X-Forwarded-Proto. Ensure that header is set and the app honours it. - Certbot fails to validate โ DNS is not yet pointing at the server, or port 80 is blocked. Confirm the A record and firewall.
- WebSocket disconnects โ the
UpgradeandConnectionheaders are missing from thelocationblock. - Changes ignored โ you edited
sites-availablebut the symlink insites-enabledpoints elsewhere, or you forgotsystemctl reload nginx.
Where to go next
A single Nginx reverse proxy fronts one host well, but production traffic eventually needs multiple backends and health checks. That is exactly what a cloud load balancer provides โ see our walkthrough on setting up an Octavia load balancer in OpenStack. To keep Nginx itself running cleanly across reboots and updates, review managing systemd services.
Conclusion
You installed Nginx, configured a reverse proxy with the correct forwarding headers, verified it routes to your backend, and secured it with an auto-renewing Letโs Encrypt certificate. This pattern fronts internal apps safely on a single server. When you need cloud-scale high availability across many backends, clouditiv runs managed load balancing on a sovereign, ISOย 27001 / BSIย C5 private cloud built on Ubuntu 24.04 + OpenStack 2025.2, with your data in Germany. Explore our on-premise cloud solution.