Nginx Reverse Proxy and Deployment

Introduction

Nginx terminates HTTP on ports 80/443 and forwards traffic to your Java or Node process on localhost. It also serves static frontends efficiently. This chapter installs Nginx, configures proxy_pass, validates config, and walks a practical deploy checklist for a small VPS.

Prerequisites

Install and Start Nginx

bash
sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
curl -I http://127.0.0.1/

Default site root: /var/www/html/. Place a test page:

bash
echo '<h1>hello_code</h1>' | sudo tee /var/www/html/index.html
curl http://127.0.0.1/

Config layout on Debian/Ubuntu:

PathRole
/etc/nginx/nginx.confMain file
/etc/nginx/sites-available/Site definitions
/etc/nginx/sites-enabled/Symlinks to enabled sites
/var/log/nginx/Access and error logs

Static Site Only

/etc/nginx/sites-available/mysite:

nginx
server {
    listen 80;
    server_name example.com www.example.com;
 
    root /var/www/mysite;
    index index.html;
 
    location / {
        try_files $uri $uri/ =404;
    }
}

Enable:

bash
sudo mkdir -p /var/www/mysite
sudo cp -r ./dist/* /var/www/mysite/
sudo ln -sf /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Reverse Proxy to Spring Boot (8080)

Reload after edit:

bash
sudo nginx -t && sudo systemctl reload nginx

Tip

Bind App to Localhost

Run Java/Node on 127.0.0.1:8080 so the port is not exposed publicly without Nginx—defense in depth with firewall.

Reverse Proxy to Node (3000)

WebSocket-friendly headers matter for some Next.js or Socket setups.

TLS (Awareness)

Production needs HTTPS—typically Certbot with Let’s Encrypt:

bash
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com

Certbot edits Nginx for you. Renewals run via systemd timer.

Deploy Checklist

Use this before and after each release:

Before deploy

  • df -h — enough disk (disk chapter)
  • Backup DB or snapshot if required
  • Tag release in Git

Deploy steps

  • Upload artifact: scp / rsync / CI (networking)
  • sudo systemctl restart myapp or pm2 restart my-api
  • sudo nginx -t && sudo systemctl reload nginx if config changed

Verify

  • sudo ss -tlnp | grep -E ':80|:8080|:3000'
  • curl -sf http://127.0.0.1:8080/actuator/health (adjust path)
  • curl -I http://your-domain/ through public DNS
  • sudo ufw status or cloud security group allows 80/443

Logs

bash
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
sudo journalctl -u myapp -f

Rollback

  • Previous JAR/archive kept in /opt/myapp/releases/
  • systemctl restart with old binary if health check fails

Mini Example: Proxy + Health Script

bash
# App health on backend
curl -sf http://127.0.0.1:8080/health
 
# Through Nginx (after config)
curl -sf http://127.0.0.1/health

Automate with shell scripts post-deploy.

FAQ

502 Bad Gateway?

Backend down, wrong proxy_pass port, or app bound only to a different interface—check ss -tlnp and app logs.

413 Request Entity Too Large?

Increase client_max_body_size in Nginx for big uploads.

Changes not visible?

Browser cache vs reload nginx—also confirm you edited sites-enabled file.

Nginx vs Apache?

Both work; Nginx is common for static + reverse proxy on small VPS setups covered here.

What comes next?

Monitoring and troubleshooting.