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:

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;
    }
}

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

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