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.