Skip to content

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, not ports)
  • 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