Docker hinter einem Reverse Proxy - Grundprinzip und häufige Fehler

Docker hinter einem Reverse Proxy - Grundprinzip und häufige Fehler

Ein Reverse Proxy vor Docker-Containern ist das Standardmuster für Produktionsumgebungen — hier ist warum und wie man die typischen Fehler vermeidet.

Warum ein Reverse Proxy?

Ohne Proxy müsste jeder Dienst auf einem eigenen Port laufen (:8080, :8081, ...). Ein Reverse Proxy löst mehrere Probleme auf einmal:

  • Ein Einstiegspunkt auf Port 80/443 für alle Dienste
  • TLS-Terminierung an einer Stelle statt in jedem Container
  • Host-basiertes Routing: app.example.com → Container A, api.example.com → Container B
  • Sicherheit: Interne Container sind nicht direkt erreichbar

Das richtige Netzwerk-Setup

services:
  proxy:
    image: nginx
    ports:
      - "80:80"       # Nur der Proxy exponiert Ports
      - "443:443"
    networks:
      - frontend
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

  app:
    image: myapp
    # Keine ports: — nicht direkt erreichbar
    networks:
      - frontend      # Erreichbar für den Proxy
      - backend       # Erreichbar für die Datenbank

  db:
    image: postgres:16
    # Keine ports: — nur intern erreichbar
    networks:
      - backend

networks:
  frontend:
  backend:

Der Schlüssel: Nur der Proxy exponiert Ports nach außen. App-Container sind nur über interne Docker-Netzwerke erreichbar.

Nginx-Konfiguration als Reverse Proxy

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://app:8080;    # Container-Name als DNS
        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;
    }
}

Häufige Fehler

Fehler 1: Container exponiert direkt auf 0.0.0.0

# FALSCH: Umgeht den Proxy komplett
app:
  ports:
    - "8080:8080"   # Jeder kann direkt auf :8080 zugreifen

# RICHTIG: Kein ports:-Block, nur internes Netzwerk
app:
  networks:
    - frontend

Wenn ein Container über ports: erreichbar ist, kann ein Angreifer den Proxy umgehen — inkl. aller Authentifizierungs- und Rate-Limit-Regeln.

Fehler 2: Falsche IP in Logs und Redirects

Ohne die X-Forwarded-*-Header sieht die Anwendung die interne Docker-IP des Proxys statt der echten Client-IP. Redirects zeigen http:// statt https://.

Die Anwendung muss dem Proxy vertrauen und die Headers auswerten. Beispiel für eine PHP-App:

// Nur wenn der Proxy vertrauenswürdig ist (internes Netzwerk)
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
    $_SERVER['HTTPS'] = $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ? 'on' : 'off';
}

Fehler 3: Falsches Netzwerk

Container müssen im selben Docker-Netzwerk sein, damit der Proxy sie per Name ansprechen kann:

# FALSCH: Proxy und App in verschiedenen Netzwerken
proxy:
  networks: [proxy-net]
app:
  networks: [app-net]   # Proxy kann "app" nicht auflösen

# RICHTIG: Gemeinsames Netzwerk
proxy:
  networks: [shared]
app:
  networks: [shared]

Fehler 4: Docker-Port 2375 ohne TLS

# NIEMALS so exponieren:
dockerd -H tcp://0.0.0.0:2375   # Unverschlüsselt, keine Auth = Root-Zugriff auf den Host

WebSockets und lange Verbindungen

Für WebSocket-Verbindungen braucht Nginx zusätzliche Header:

proxy_http_version 1.1;
proxy_set_header Upgrade    $http_upgrade;
proxy_set_header Connection "upgrade";

Der Reverse Proxy ist keine optionale Schicht — er ist der einzige sichere Weg, mehrere Docker-Dienste auf einem Host zu betreiben.