π Nginx Configuration for Node.js Apps (PM2 + Ubuntu)

This article is a personal reference guide for configuring Nginx as a reverse proxy in front of a Node.js application managed by PM2 on Ubuntu.
It focuses on what actually matters in production.
π§ Why Nginx?
Nginx sits between the internet and your Node app.
Responsibilities:
Accept public traffic (port 80 / 443)
Forward requests to Node (localhost)
Handle large payloads (PDFs, uploads)
Enable HTTPS
Improve security & stability
Node should not be directly exposed to the internet.
π¦ Install Nginx
sudo apt update
sudo apt install -y nginx
Verify:
systemctl status nginx
Test:
curl http://localhost
π Nginx Directory Structure (Important)
/etc/nginx/
βββ nginx.conf
βββ sites-available/
β βββ my-app
βββ sites-enabled/
βββ my-app -> ../sites-available/my-app
Rule:
Write configs in
sites-availableEnable them using a symlink in
sites-enabled
π Check Existing Configurations
ls /etc/nginx/sites-available
ls /etc/nginx/sites-enabled
View loaded configs:
sudo nginx -T
βοΈ Basic Reverse Proxy Configuration
Create a config file:
sudo nano /etc/nginx/sites-available/my-app
server {
listen 80;
server_name YOUR_DOMAIN_OR_IP;
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_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;
proxy_cache_bypass $http_upgrade;
}
}
π Enable the Site
sudo ln -s /etc/nginx/sites-available/my-app \
/etc/nginx/sites-enabled/
Disable default site (recommended):
sudo rm /etc/nginx/sites-enabled/default
π§ͺ Validate & Reload
sudo nginx -t
sudo systemctl reload nginx
π§ Node.js Best Practice
Bind Node only to localhost:
app.listen(3000, "127.0.0.1");
This prevents direct external access.
π Handling Large Requests (PDFs, Uploads)
Add inside server {}:
client_max_body_size 50M;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
send_timeout 300;
This avoids timeout failures for long-running tasks.
π Logs & Debugging
Nginx
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
Node (PM2)
pm2 logs my-app
π Enable HTTPS (Letβs Encrypt)
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
Auto-renew test:
sudo certbot renew --dry-run
π§ Production Checklist
β Node running via PM2
β App bound to localhost
β Nginx reverse proxy enabled
β Large payload support
β HTTPS enabled
β Auto-restart on reboot
π§― Common Mistakes
β Running Node on port 80
β Exposing Node publicly
β Installing OS packages via npm
β Forgetting timeouts for long jobs
β Editing
nginx.confdirectly
π Final Thoughts
Nginx + PM2 is a battle-tested setup for Node.js apps.
Once configured properly:
Deployments are safer
Restarts are painless
Scaling is easier
Debugging is predictable
This setup has saved me hours during production incidents.



