Reverse Proxy
PegaProx works behind nginx, Apache, Caddy, or any other reverse proxy. A production-ready nginx configuration is included at examples/nginx.conf.
Setup
-
Enable Settings → Server → Behind Reverse Proxy in PegaProx. This disables built-in SSL and binds to
127.0.0.1only. -
Copy
examples/nginx.confto/etc/nginx/sites-available/pegaproxand adjustserver_nameand SSL certificate paths. -
Enable the site:
ln -s /etc/nginx/sites-available/pegaprox /etc/nginx/sites-enabled/ -
Test and reload:
nginx -t && systemctl reload nginx
Docker: also set PEGAPROX_BEHIND_PROXY=true
In a container deployment, set the environment variable PEGAPROX_BEHIND_PROXY=true in addition to (or instead of) the Settings toggle above.
- The container healthcheck reads this variable to choose its probe protocol. If you enable reverse-proxy mode only via the UI toggle without the env var, the healthcheck still probes
https://against the now-plain-HTTP socket - harmless (it falls back to HTTP and the container stays healthy), but it spams TLS-handshake noise (Invalid HTTP method '\x16\x03\x01…') into the container logs. - The env var also routes the console (VNC/SSH) WebSockets through the proxy port, so consoles work without separately flipping the UI toggle.
nginx Configuration
The included config handles all PegaProx components: main app, SSE live updates, VNC console WebSocket, and SSH console WebSocket.
upstream pegaprox_app {
server 127.0.0.1:5000;
}
upstream pegaprox_vnc {
server 127.0.0.1:5001;
}
upstream pegaprox_ssh {
server 127.0.0.1:5002;
}
server {
listen 80;
server_name pegaprox.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name pegaprox.example.com;
ssl_certificate /etc/ssl/certs/pegaprox.pem;
ssl_certificate_key /etc/ssl/private/pegaprox.key;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 100g; # match PegaProx upload limit for ISOs
proxy_connect_timeout 10;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
# Main app + WebSocket
location / {
proxy_pass http://pegaprox_app;
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
# SSE - buffering must be off
location /api/sse/ {
proxy_pass http://pegaprox_app;
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_buffering off;
proxy_cache off;
proxy_read_timeout 86400;
}
# VNC console websocket (port 5001)
location ~ ^/api/clusters/.*/vncwebsocket {
proxy_pass http://pegaprox_vnc;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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_read_timeout 3600;
}
# SSH console websocket (port 5002)
location ~ ^/api/clusters/.*/shellws {
proxy_pass http://pegaprox_ssh;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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_read_timeout 3600;
}
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
PegaProx trusts X-Forwarded-For only from loopback addresses (127.0.0.1 / ::1). If the reverse proxy runs on a different host, add its IP to Settings → Server → Trusted Proxies.
The client_max_body_size must match PegaProx's upload limit (default 100 GB). Without this, nginx rejects large ISO uploads with a 413 error.
Traefik Configuration
Add the following labels to your PegaProx Docker Compose service:
yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.pegaprox.rule=Host(`pegaprox.example.com`)"
- "traefik.http.routers.pegaprox.entrypoints=websecure"
- "traefik.http.routers.pegaprox.tls.certresolver=letsencrypt"
- "traefik.http.services.pegaprox.loadbalancer.server.port=5000"
- "traefik.http.services.pegaprox.loadbalancer.server.scheme=https"
- "traefik.http.services.pegaprox.loadbalancer.serversTransport=insecureTransport@file"
Note: Traefik needs serversTransport with insecureSkipVerify: true because PegaProx uses self-signed certs internally.
WebSocket Support for VNC/SSH
VNC and SSH WebSocket connections need to be routed to separate backend ports (5001 and 5002). Add to Traefik dynamic config (file provider):
yaml
http:
routers:
pegaprox_vnc:
entrypoints: ["https"]
rule: "Host(`pegaprox.example.com`) && PathRegexp(`/api/clusters/[a-z0-9]+/vms/.+/vncwebsocket`)"
service: pegaprox_vnc
tls:
certResolver: letsEncrypt
pegaprox_ssh:
entrypoints: ["https"]
rule: "Host(`pegaprox.example.com`) && PathRegexp(`/api/clusters/[a-z0-9]+/nodes/.+/shellws`)"
service: pegaprox_ssh
tls:
certResolver: letsEncrypt
services:
pegaprox_vnc:
loadBalancer:
servers:
- url: "http://127.0.0.1:5001"
pegaprox_ssh:
loadBalancer:
servers:
- url: "http://127.0.0.1:5002"
The VNC/SSH routers must be defined before the main catch-all router so Traefik matches them first. Adjust ports if PegaProx runs on non-default ports.
Nginx Proxy Manager (NPM)
NPM does not natively support regex location blocks or SSE tuning through its GUI. You need two extra steps: a Custom Location for SSE and Custom Nginx Configuration for WebSockets.
Step 1: SSE Custom Location
In your proxy host, add a Custom Location for /api/sse/ pointing to port 5000. NPM already injects proxy_set_header Host, X-Forwarded-For and X-Forwarded-Proto into the location block it generates, so this Custom Location only needs the SSE-specific tuning - do not re-add proxy_set_header Host here:
nginx
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400;
Why no Host line? Adding proxy_set_header Host $host; on top of the one NPM already sets produces a doubled Host header, which the WSGI layer folds into Host: your-domain, your-domain. Builds up to 0.9.15 then reject the body-less SSE-token POST with 403 CSRF validation failed. PegaProx tolerates the folded header from the next release onward (issue #626), but the cleanest setup is simply not to duplicate the directive.
Step 2: Custom Nginx Configuration
In your proxy host's Advanced → Custom Nginx Configuration field, paste the following for WebSocket support (VNC + SSH consoles) and upload limits:
nginx
client_max_body_size 100g;
proxy_connect_timeout 10;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
location ~ ^/api/clusters/.*/vncwebsocket {
proxy_pass http://YOUR_IP:5001;
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_read_timeout 3600;
}
location ~ ^/api/clusters/.*/shellws {
proxy_pass http://YOUR_IP:5002;
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_read_timeout 3600;
}
Step 3: NPM Settings
-
Enable "Websockets Support" in the proxy host settings.
-
Replace
YOUR_IPwith PegaProx's internal IP address (e.g.,172.17.0.2for Docker, or127.0.0.1if on the same host). -
Set the proxy host scheme to https and port to 5000.
Docker users: Do not enable the "Behind Reverse Proxy" option in PegaProx with 0.0.0.0 as trusted proxy - this trusts all IPs. Instead, add your reverse proxy's subnet (e.g., 172.17.0.0/16) to Settings → Server → Trusted Proxies.
Port Reference
PegaProx uses three ports. VNC and SSH are always relative to the main port:
| Service | Default Port | Formula |
|---|---|---|
| Web UI + API | 5000 | main_port |
| VNC Console | 5001 | main_port + 1 |
| SSH Console | 5002 | main_port + 2 |
If you changed the main port (e.g., to 443), adjust VNC (444) and SSH (445) accordingly in your reverse proxy config.