Nginx Reverse Proxy
Overview
Nginx is used as a reverse proxy to:
- Terminate TLS (HTTPS)
- Route traffic to OpenEMS services
- Handle WebSocket connections (Edge ↔ Backend, UI)
- Expose only port 443 (and 80 for ACME)
Architecture
Internet
↓
Nginx (443 / 80)
↓
-------------------------
| openems-ui (port 80) |
| openems-backend |
| - 8081 (Edge WS) |
| - 8082 (UI WS) |
-------------------------
Ports and Exposure
| Port | Purpose | External Access |
|---|---|---|
| 80 | ACME challenge + redirect | Yes |
| 443 | HTTPS entrypoint | Yes |
| 8079 | Felix console | Optional |
| 8081 | Edge WebSocket | No |
| 8082 | UI WebSocket | No |
Critical Rule:
Do not expose ports 8081 and 8082 publicly.
They must only be accessible via the reverse proxy.
HTTP → HTTPS Redirect
server {
listen 80;
server_name zevplus.ch ems.zevplus.ch;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
HTTPS Configuration
server {
listen 443 ssl;
server_name ems.zevplus.ch;
ssl_certificate /etc/letsencrypt/live/zevplus.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/zevplus.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
}
WebSocket Handling
Required for:
- Edge → Backend communication
- UI → Backend communication
Upgrade Mapping
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
OpenEMS UI
location / {
proxy_pass http://openems-ui:80;
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;
}
Edge → Backend WebSocket
location /edge-to-backend {
proxy_pass http://openems-backend:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
}
Common Issues
1. WebSocket not connecting
Symptoms: - Edge disconnects - UI shows "server not reachable"
Typical causes:
- Missing Upgrade / Connection headers
- Wrong path (/edge-to-backend)
- Backend not reachable in Docker network
2. 502 Bad Gateway
Cause: - Target container not running - Wrong service name
Debug:
docker ps
docker logs nginx
3. TLS / Certbot issues
Symptoms: - Certificate not issued - HTTP challenge fails
Check:
curl http://ems.zevplus.ch/.well-known/acme-challenge/test
4. Redirect loops
Cause:
- Incorrect X-Forwarded-Proto
- Double redirect config
Security Notes
- Only ports 80 and 443 should be publicly accessible
- Backend ports must not be exposed
- Use strong TLS configuration
- Keep certificates up to date
Best Practices
- Use internal Docker networking (
expose, notports) - Keep reverse proxy as single entrypoint
- Log WebSocket traffic separately if needed
Reload Nginx after config changes:
docker compose exec nginx nginx -s reload
Summary
- Nginx is the central entry point
- All external traffic flows through HTTPS (443)
- WebSockets must be explicitly configured
- Internal ports must never be exposed directly