Q&A Klassiker: nginx / Apache â 502, 504, Config-Fehler
Kategorie: Q&A · Channel: #klassiker
Format: BĂŒhnenspiel â Admin · Angreifer · Kunde
Die Situation
Uhrzeit: 09:47 Uhr (Morgenpeak)
Alarm: HTTP 502 Bad Gateway / 504 Gateway Timeout
Symptom: Website nicht erreichbar, API antwortet nicht
Kunde: meldet sich sofort
đšâđ» Admin-Perspektive: Was sehe ich, was tue ich?
Schritt 1: Welcher Fehler genau?
HTTP 502 Bad Gateway
â nginx/Apache erreicht Backend aber bekommt ungĂŒltige Antwort
â Backend (PHP-FPM, App-Server, Upstream) ist kaputt oder crashed
HTTP 504 Gateway Timeout
â nginx/Apache erreicht Backend nicht rechtzeitig
â Backend ist zu langsam oder gar nicht erreichbar
HTTP 503 Service Unavailable
â Backend ĂŒberlastet oder explizit down
HTTP 500 Internal Server Error
â Backend antwortet aber wirft Fehler
â meist Applikationsfehler, nicht nginx/Apache selbst
Schritt 2: Logs lesen
# nginx Error-Log
tail -50 /var/log/nginx/error.log
tail -f /var/log/nginx/error.log # live
# Apache Error-Log
tail -50 /var/log/apache2/error.log
tail -f /var/log/apache2/error.log
# Typische 502 Meldung nginx:
# connect() failed (111: Connection refused) while connecting to upstream
# â PHP-FPM lĂ€uft nicht!
# Typische 504 Meldung nginx:
# upstream timed out (110: Connection timed out)
# â Backend zu langsam (DB? Slow Query?)
# Access-Log: welche URLs betroffen?
tail -100 /var/log/nginx/access.log | grep " 502 \| 504 "
tail -100 /var/log/apache2/access.log | grep " 502 \| 504 "
Schritt 3: Backend-Dienste prĂŒfen
# PHP-FPM Status
systemctl status php8.1-fpm
systemctl status php7.4-fpm
systemctl status php7.2-fpm
# PHP-FPM neu starten (schnelle Lösung)
systemctl restart php8.1-fpm
# PHP-FPM Socket vorhanden?
ls -la /run/php/
# php8.1-fpm.sock sollte da sein
# PHP-FPM Log
tail -30 /var/log/php8.1-fpm.log
journalctl -u php8.1-fpm -n 30
# Upstream App-Server (Node, Python, Java...)
systemctl status myapp
netstat -tlnp | grep :3000 # lÀuft der App-Server?
curl -s http://localhost:3000/health # antwortet er?
Schritt 4: nginx Konfiguration prĂŒfen
# Config-Syntax testen (IMMER vor reload!)
nginx -t
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# nginx neu laden (kein Verbindungsabbruch)
systemctl reload nginx
# NICHT: systemctl restart nginx (trennt aktive Verbindungen)
# nginx Status
systemctl status nginx
nginx -v # Version
# Konfiguration debuggen
nginx -T # komplette effektive Config ausgeben
nginx -T | grep -A 5 "upstream" # Upstream-Definitionen
Schritt 5: Typische nginx Upstream-Config
# /etc/nginx/sites-available/example.com
upstream php_backend {
server unix:/run/php/php8.1-fpm.sock;
# oder TCP:
# server 127.0.0.1:9000;
}
upstream app_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001 backup; # Fallback
keepalive 32;
}
server {
listen 80;
server_name example.com;
# Timeouts â bei 504 hier anpassen:
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s; # â bei langsamen Backends erhöhen
location / {
proxy_pass http://app_backend;
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;
}
location ~ \.php$ {
fastcgi_pass php_backend;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 60s; # â bei langsamen PHP-Scripts erhöhen
}
}
Schritt 6: Apache Konfiguration prĂŒfen
# Config-Syntax testen
apache2ctl configtest
apachectl configtest
# Apache neu laden
systemctl reload apache2
# Apache Status und Module
apache2ctl -M | grep -E "proxy|php|rewrite"
# PHP-Modul aktiv?
a2query -m php8.1 # Debian/Ubuntu
httpd -M | grep php # RHEL
# Virtualhosts anzeigen
apache2ctl -S
apachectl -S
# Typische Apache PHP-FPM Config:
# /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/html
# PHP via FPM (mod_proxy_fcgi)
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
</FilesMatch>
# Proxy Timeout (bei 504 hier anpassen)
ProxyTimeout 60
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
Schritt 7: Verbindung zum Backend direkt testen
# TCP-Verbindung testen
curl -v http://localhost:3000/
telnet localhost 3000
# PHP-FPM Socket direkt testen
cgi-fcgi -bind -connect /run/php/php8.1-fpm.sock
# (apt install libfcgi-dev)
# Upstream von auĂen erreichbar? (sollte es NICHT sein!)
curl http://server-ip:9000/ # PHP-FPM
curl http://server-ip:3000/ # App-Server
đŠč Angreifer-Perspektive: Was nutze ich aus?
nginx Fehlermeldungen als Reconnaissance
502/504 Fehler verraten:
â welches Backend lĂ€uft (PHP-FPM Version im Header)
â interne IPs wenn Debug-Modus aktiv
â Technologie-Stack
curl -I https://www.ziel.de
# Server: nginx/1.18.0
# X-Powered-By: PHP/7.4.33 â Version sichtbar!
Fix:
server_tokens off; # nginx Version verstecken
# php.ini: expose_php = Off # PHP Version verstecken
Direkt zugÀngliche Backend-Ports
HĂ€ufiger Fehler: PHP-FPM oder App-Server lauscht auf 0.0.0.0
netstat -tlnp | grep :9000
# tcp 0 0 0.0.0.0:9000 â PHP-FPM von ĂŒberall erreichbar!
Angriff:
# SSRF oder direkte FPM-Anfrage möglich
# PHP-FPM ohne Auth = Remote Code Execution möglich
# CVE-2019-11043: nginx + PHP-FPM = RCE
Fix:
# PHP-FPM auf Socket oder 127.0.0.1 beschrÀnken:
# /etc/php/8.1/fpm/pool.d/www.conf:
listen = /run/php/php8.1-fpm.sock
# NICHT: listen = 0.0.0.0:9000
HTTP Request Smuggling
Bei falsch konfigurierten Proxys:
â Angreifer schmuggelt zweite HTTP-Anfrage in erste
â Backend verarbeitet versteckten Request
â Auth-Bypass möglich
Erkennbar wenn:
â nginx und Backend interpretieren
Content-Length und Transfer-Encoding unterschiedlich
PrÀvention:
proxy_http_version 1.1;
proxy_set_header Connection "";
đ Kunden-Perspektive: Was erlebe ich?
09:47 Uhr â Nachricht:
"Die Website zeigt 'Bad Gateway'.
Unsere Kunden können nicht bestellen.
Das passiert jeden Morgen gegen 9-10 Uhr
wenn viel Betrieb ist. Was ist das Problem?"
â "jeden Morgen" = wichtiger Hinweis!
Kein zufĂ€lliger Fehler â reproduzierbar!
Ursache bei "tÀglich morgens": PHP-FPM Pool erschöpft
# PHP-FPM Pool-Config prĂŒfen:
cat /etc/php/8.1/fpm/pool.d/www.conf | grep -E "pm\.|max_children"
# Typische Standardkonfig (zu klein fĂŒr Traffic-Peaks):
# pm = dynamic
# pm.max_children = 5 â nur 5 PHP-Prozesse gleichzeitig!
# pm.start_servers = 2
# pm.min_spare_servers = 1
# pm.max_spare_servers = 3
# Angepasst fĂŒr Traffic-Peaks:
# pm.max_children = 50
# pm.max_requests = 500 â Memory-Leak-Schutz
# Aktuellen Pool-Status anzeigen:
curl http://localhost/php-fpm-status # wenn status page konfiguriert
# oder:
systemctl status php8.1-fpm | grep "processes"
Kommunikation mit dem Kunden:
09:55 Uhr:
"Wir haben die Ursache identifiziert: Der PHP-Prozess-Pool
war fĂŒr den Morgen-Traffic zu klein konfiguriert.
Bei mehr als 5 gleichzeitigen Anfragen entstanden Wartezeiten.
Wir haben die KapazitÀt auf 50 Prozesse erhöht.
Das Problem sollte morgen frĂŒh nicht mehr auftreten."
đŻ Erkenntnis â Drei Perspektiven zusammen
| Perspektive |
Kernaussage |
| đšâđ» Admin |
nginx -t vor jedem reload · tail -f error.log bei 502 |
| đŠč Angreifer |
PHP-FPM auf 0.0.0.0 = direkter Angriffsvektor |
| đ Kunde |
"tÀglich morgens" = PHP-FPM Pool zu klein, nicht Zufall |
đ§ PrĂ€vention
# 1. server_tokens off in nginx.conf
# 2. expose_php = Off in php.ini
# 3. PHP-FPM nur auf Socket oder 127.0.0.1
# 4. PHP-FPM Pool-GröĂe ans Traffic-Profil anpassen
# 5. nginx Status-Page fĂŒr Monitoring
# stub_status on; in nginx.conf
# 6. PHP-FPM Status-Page aktivieren
# pm.status_path = /php-fpm-status
# 7. Timeout-Werte auf Backend-Performance abstimmen
# 8. Monitoring: Alert bei HTTP 502/504 > 5/min
đ Schnellreferenz: nginx/Apache 502/504 â Einzeiler
# Diagnose
tail -f /var/log/nginx/error.log # live errors
systemctl status php8.1-fpm # FPM lÀuft?
ls -la /run/php/ # Socket vorhanden?
curl -v http://localhost:3000/health # Backend direkt
# Config-Test (IMMER vor reload!)
nginx -t && systemctl reload nginx
apache2ctl configtest && systemctl reload apache2
# PHP-FPM neu starten
systemctl restart php8.1-fpm
# Pool-Config
grep -E "pm\.|max_children" /etc/php/*/fpm/pool.d/www.conf
# Verbindungen zum Backend
ss -tnp | grep :9000 # PHP-FPM Verbindungen
netstat -tlnp | grep nginx # nginx lauscht?