Die vier Modi im Überblick
PHP kann auf vier fundamental verschiedene Weisen ausgeführt werden. Jeder Modus hat einen anderen tradeoff zwischen Performance, Isolation und Administrations-Aufwand:
| Modus | Prozessmodell | Performance | Isolation | Admin-Aufwand | Typischer Einsatz |
|---|---|---|---|---|---|
| mod_php | PHP im Apache-Prozess | Sehr hoch | Niedrig | Niedrig | Shared Hosting (veraltet) |
| CGI / FastCGI | PHP-Prozess pro Request (CGI) oder persistent (FastCGI) | Mittel | Mittel | Mittel | Shared Hosting, günstige VPS |
| PHP-FPM | Pool von PHP-Prozessen, gemanagt von einem Master | Sehr hoch | Hoch | Mittel | Production VPS, Managed Hosting |
| PHP-CLI | Kein Webserver — Kommandozeile | N/A | N/A | N/A | Cronjobs, Backend-Scripts, CLI-Tools |
mod_php ist ein Relikt — vermeide es wenn möglich
mod_php lädt PHP direkt in den Apache-Worker-Prozess. Das bedeutet: alle Websites auf dem Server teilen sich denselben User (www-data) und dieselbe php.ini — eine kompromittierte Site kann alle anderen lesen. Modernes Hosting nutzt PHP-FPM mit isolierten Pools pro User. Wenn dein Hoster noch mod_php anbietet, ist das ein Zeichen veralteter Infrastruktur.
PHP-FPM: Der Production-Standard
PHP-FPM (FastCGI Process Manager) ist der Modus, den jedes moderne Production-Setup nutzt. Die Idee: ein separater PHP-Masterprozess verwaltet einen Pool von Child-Prozessen, die auf Anfragen warten. Das gibt dir:
- Pro-User/Pool-Isolation — Jede Website kann einen eigenen Pool mit eigenen Ressourcen-Limits haben
- Persistenz — PHP-Prozesse bleiben warm, kein Startup-Overhead pro Request
- Graceful Reload — Config-Änderungen ohne downtime:
systemctl reload php8.2-fpm - Dynamic/On-Demand Scaling — Prozesse starten bei Bedarf und sterben nach Leerlauf
PHP-FPM Pool-Konfiguration (pro Website)
Jede Website bekommt einen eigenen FPM-Pool mit eigenem User — das ist die wichtigste Sicherheitsmaßnahme:
# /etc/php/8.2/fpm/pool.d/meine-website.conf
[meine-website]
user = meine-website
group = meine-website
listen = /run/php/meine-website.sock
listen.owner = www-data
listen.group = www-data
# Process Manager: dynamic (start/spare/max)
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8
pm.max_requests = 500 # Recycle nach 500 Requests (Memory-Leak-Schutz)
# Timeouts
request_terminate_timeout = 30s
request_slowlog_timeout = 10s
# Logging
slowlog = /var/log/php-fpm/meine-website-slow.log
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/log/php-fpm/meine-website-error.log
# Memory-Limit
php_admin_value[memory_limit] = 256M
Warum separate Pools pro Website?
Bei einer Shared-Installation ohne Isolation kann ein schlecht programmiertes WordPress-Plugin den gesamten Server lahmlegen — es frisst alle PHP-Prozesse. Mit separaten Pools pro User wird jede Website in ihrer eigenen Sandkiste ausgeführt. Wenn Site A 50 PHP-Prozesse verbraucht, sind die Prozesse von Site B davon nicht betroffen. Du kannst sogar pm.max_children pro Pool individuell setzen — eine größere Site bekommt mehr Ressourcen als eine kleine.
PHP-FPM mit Nginx konfigurieren
Nginx allein kann kein PHP — es leitet PHP-Requests an den PHP-FPM-Prozess weiter. Die Konfiguration:
# /etc/nginx/sites-available/meine-website
server {
listen 80;
server_name meine-website.de www.meine-website.de;
root /var/www/meine-website/public;
index index.php index.html;
# PHP an PHP-FPM weiterleiten
location ~ \\.php$ {
fastcgi_pass unix:/run/php/meine-website.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# Timeouts
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
}
# Kein PHP-Execution außerhalb des public-Verzeichnisses
location ~ ^/uploads/.*\\.php$ {
deny all;
}
# Statische Assets cachen
location ~* \\.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# WordPress Permalinks
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
PHP-FPM mit Apache (mpm-event + PHP-FPM)
Apache kann PHP-FPM nutzen via mod_proxy_fcgi. Das ist die empfohlene Konfiguration für Apache-basierte Setups:
# Apache 2.4 + PHP-FPM (kein mod_php)
# Enable required modules:
a2enmod proxy_fcgi setenvif rewrite
# PHP-FPM via ProxyPassMatch
<VirtualHost *:80>
ServerName meine-website.de
<FilesMatch \\.php$>
SetHandler "proxy:unix:/run/php/meine-website.sock|fcgi://localhost/"
</FilesMatch>
DocumentRoot /var/www/meine-website/public
<Directory /var/www/meine-website/public>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
FastCGI vs. PHP-FPM — was ist der Unterschied?
Die Verwirrung ist berechtigt: FastCGI und PHP-FPM werden oft synonym verwendet, aber technisch ist FPM eine spezifische FastCGI-Implementierung mit erweitertem Process-Management.
| Eigenschaft | CGI | FastCGI | PHP-FPM |
|---|---|---|---|
| Prozess-Start | Jeder Request = neuer Prozess | Persistenter Prozess-Pool | Persistenter Pool mit Master/Child-Modell |
| Startup-Overhead | Hoch (PHP pro Request neu geladen) | Niedrig (Prozesse bleiben warm) | Niedrig + Opcode-Cache möglich |
| Process Management | None (Apache/Server verwaltet) | Basic (persistent, aber statisch) | Dynamisch (on-demand, recyceln, max-requests) |
| Pooling pro User | Nein | Möglich, aber kompliziert | Native, pro Pool-Konfiguration |
| Status-Endpoint | Nein | Basic | Ja — /status?full |
| Slow-Log | Nein | Nein | Ja — PHP-FPM slowlog |
Empfehlung: PHP-FPM mit Unix Socket
Nutze Unix Sockets statt TCP-Ports für PHP-FPM. Unix Sockets sind ~30% schneller (kein TCP-Overhead) und vermeiden Security-Risiken durch offene Netzwerkports. Der Socket muss dem Webserver-User (www-data) gehören: listen.owner = www-data; listen.group = www-data.
PHP-FPM Performance-Tuning
Die Standard-PHP-FPM-Konfiguration ist für die meisten Setups viel zu großzügig. Hier die wichtigsten Hebel:
pm = dynamic vs. ondemand vs. static
# dynamic: permanente Warmhaltung (gut für consistent Traffic)
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
# ondemand: startet nur bei Bedarf, spart RAM bei wenig Traffic
# Gut für Server mit vielen kleinen Sites
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 60s
# static: fixes Prozess-Count, keine dynamische Anpassung
# Gut für consistent hohe Last
pm = static
pm.max_children = 20
Opcode-Cache aktivieren (OPcache)
OPcache kompiliert PHP-Scripts einmal und cached das Ergebnis. Das ist der größte einzelne Performance-Gewinn für PHP:
# php.ini — OPcache aktivieren
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=0
opcache.fast_shutdown=1
# Für heavy WordPress: mehr memory
opcache.memory_consumption=512
Realpath-Cache
# php.ini — Realpath-Cache für bessere Datei-Resolve-Performance
realpath_cache_size=4096K
realpath_cache_ttl=600
PHP-CLI: Cronjobs und Backend-Scripts
PHP-CLI läuft ohne Webserver — wichtig für Cronjobs, Migrations, Wartungsscripts und CLI-Tools. Es hat keinen Request-Overhead, nutzt aber dieselbe php.ini (oder eine eigene):
# CLI: eigene php.ini mit höherem Memory-Limit
php -d memory_limit=512M /var/www/meine-website/scripts/migrate.php
# Cronjob mit PHP-CLI
# Crontab:
# 0 3 * * * php /var/www/meine-website/scripts/cleanup.php >> /var/log/cleanup.log 2>&1
# WordPress WP-CLI
# WP-CLI nutzt PHP-CLI mit eigenem Environment
wp --path=/var/www/wordpress cron event run --due-now
php.ini für CLI vs. FPM trennen
CLI und FPM brauchen unterschiedliche Einstellungen. CLI braucht oft mehr Memory für Migration-Scripts, FPM sollte knapper konfiguriert sein um einzelne Requests zu begrenzen:
# CLI php.ini: /etc/php/8.2/cli/php.ini
memory_limit = 1024M # Scripts brauchen mehr
max_execution_time = 300
opcache.enable = 1 # Auch CLI profitiert
# FPM php.ini: /etc/php/8.2/fpm/php.ini
memory_limit = 256M
max_execution_time = 30
opcache.enable = 1
Sicherheits-Best Practices
Upload-Verzeichnisse nie als PHP ausführbar
WordPress-Upload-Verzeichnisse sind ein beliebter Angriffspunkt: ein Angreifer lädt eine PHP-Shell hoch und führt sie aus. Fange das ab:
# Nginx: Uploads-Verzeichnis — PHP-Dateien blockieren
location ^~ /wp-content/uploads/ {
location ~ \\.php$ {
deny all; # Kein PHP in Uploads
return 444;
}
}
- disable_functions: Setze gefährliche Funktionen auf
disable_functions = exec,passthru,shell_exec,system,proc_open,popen— es sei denn, du brauchst sie bewusst - open_basedir: Beschränke PHP auf das Website-Verzeichnis:
open_basedir = /var/www/meine-website/:/tmp/ - display_errors = Off in Production — Fehlermeldungen geben Angreifern zu viel Info
- session.cookie_httponly = 1 — verhindert XSS-Zugriff auf Session-Cookies
Monitoring: PHP-FPM Status prüfen
PHP-FPM hat einen eingebauten Status-Endpunkt, den du in Nginx einblenden kannst:
# In der FPM Pool-Konfiguration:
pm.status_path = /status
# In Nginx:
location ^~ /status {
access_log off;
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/meine-website.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Abfrage:
curl http://127.0.0.1/status
# Output:
# pool: meine-website
# process manager: dynamic
# start time: 12/Jun/2026:10:00:00 +0200
# accepted conn: 15234
# listen queue: 0
# active processes: 4
# total processes: 8
# max children reached: 0
Wichtige Metriken im Auge behalten
- listen queue > 0 → nicht genug PHP-Prozesse, Erhöhe
pm.max_children - max children reached > 0 regelmäßig → zu wenig Capacity, Budget für mehr PHP-Prozesse nötig
- slowlog regelmäßig prüfen → langsame Queries/Plugins identifizieren
Fazit: PHP-FPM ist der richtige Modus
Die korrekte Konfiguration für Production: PHP-FPM mit dynamischem Process Manager, separatem Pool pro Website, Unix Socket, aktiviertem OPcache und Realpath-Cache. Das ist nicht kompliziert — eine Abendstunde Konfigurationsarbeit — und macht den Unterschied zwischen einer Site, die 500 req/s稳稳 liefert, und einer, die bei 50 req/s zusammenbricht.
Die Faustregel für die Prozess-Konfiguration: pm.max_children so setzen, dass max_children × memory_limit nicht mehr als 50% des verfügbaren RAMs verbraucht. Bei 8 GB RAM und 256M memory_limit: max_children = 16 (4 GB für PHP, Rest für OS, Nginx, DB).
VPS mit PHP-FPM-Support
SSH-Zugang, PHP-FPM-Konfiguration, OPcache und volle Kontrolle über PHP-Einstellungen — für produktive WordPress und PHP-Apps.
Zum Hosting-Vergleich