Back to site

Nginx reverse proxy

Expose a backend application (Node, Docker, etc.) behind Nginx on your HolyCloud VPS with proxy headers and WebSocket.

Nginx reverse proxy

Rather than exposing port 3000 or 8080 on the Internet, have Nginx listen on 80/443 and forward traffic to your local application. Standard pattern on a HolyCloud Linux VPS hosting APIs, panels, or Docker containers.

Prerequisites

  • Nginx installed (apt install nginx)
  • Application listening locally (e.g. 127.0.0.1:3000)
  • Domain name and TLS certificate (see « Nginx and Certbot ») recommended
  • UFW: only 80/443 public

Step 1: example backend application

# Exemple : conteneur sur le port 3000
docker run -d --name app -p 127.0.0.1:3000:3000 votre-image:tag
curl -sI http://127.0.0.1:3000

The application should be reachable on localhost only when possible.

Step 2: reverse proxy configuration

sudo nano /etc/nginx/sites-available/app.example.com
upstream app_backend {
    server 127.0.0.1:3000;
    keepalive 32;
}

server {
    listen 80;
    listen [::]:80;
    server_name app.example.com;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name app.example.com;

    # Chemins Certbot — adaptez si nécessaire
    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    location / {
        proxy_pass http://app_backend;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 90s;
    }
}

Enable the site:

sudo ln -sf /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Step 3: multiple services (paths)

location /api/ {
    proxy_pass http://127.0.0.1:4000/;
}

location / {
    proxy_pass http://127.0.0.1:3000/;
}

Watch the trailing slash on proxy_pass to keep or strip the /api prefix.

Step 4: limits and logs

sudo nano /etc/nginx/nginx.conf

In the http block, adjust if needed:

client_max_body_size 50M;

Per-site logs: access_log /var/log/nginx/app.example.com.access.log;

Verification

curl -sI https://app.example.com
curl -s https://app.example.com/health
sudo tail -f /var/log/nginx/error.log

From outside, port 3000 must not be reachable:

sudo ufw status | grep 3000

Need help?

  • 502 Bad Gateway: backend stopped or wrong port — curl http://127.0.0.1:3000, docker ps
  • WebSocket drops: check Upgrade / Connection headers
  • Support: nginx -t, vhost excerpt (no private key), container/service status