WordPress läuft auf jedem Server. Ob es dabei schnell und sicher ist, entscheidet sich an vier Stellen und keine davon ist WordPress selbst.
Der Aufbau#
Zwei gangbare Wege: klassisch mit Paketen oder im Container. Für einen Server mit mehreren Seiten ist der Container-Weg übersichtlicher, weil PHP-Version und Erweiterungen je Seite festliegen.
services:
db:
image: mariadb:11
restart: unless-stopped
environment:
MARIADB_DATABASE: wp
MARIADB_USER: wp
MARIADB_RANDOM_ROOT_PASSWORD: "yes"
volumes: [dbdaten:/var/lib/mysql]
# kein ports: — die Datenbank braucht nichts von außen
wp:
image: wordpress:php8.3-fpm-alpine
restart: unless-stopped
depends_on: [db]
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wp
WORDPRESS_DB_USER: wp
volumes: [wpdaten:/var/www/html]
volumes: { dbdaten: , wpdaten: }
Davor der Reverse-Proxy mit Zertifikat (Caddy oder nginx), und die Datenbank bleibt im internen Netz, warum das genau so und nicht anders zusammenhängt, steht in Container-Netze verstehen.
Stelle 1: PHP-FPM nicht raten#
Die häufigste Ursache für „der Server ist zusammengebrochen“ bei WordPress ist ein zu hoch gesetztes pm.max_children. Jeder PHP-Prozess belegt Speicher; wer 50 davon erlaubt, obwohl nur 20 hineinpassen, tauscht eine langsame Seite gegen einen abgestürzten Server.
# Wie viel belegt ein Prozess tatsächlich?
ps -ylC php-fpm8.3 --sort:rss | awk '{print $8/1024 " MB"}' | tail -5
Rechnung: pm.max_children = verfügbarer RAM für PHP geteilt durch den größten gemessenen Prozess. Nicht durch den Durchschnitt, die Spitze zählt.
; /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 12 ; aus der Messung oben, nicht geraten
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500 ; beugt Speicherlecks in Erweiterungen vor
Dazu OPcache, der den kompilierten Code vorhält, ohne ihn übersetzt PHP bei jedem Aufruf alles neu:
opcache.enable=1
opcache.memory_consumption=192
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1 ; auf 0 nur, wenn nach jedem Deploy neu gestartet wird
Stelle 2: Zwischenspeicher an die richtige Stelle#
Es gibt drei Ebenen, und sie ersetzen einander nicht:
| Ebene | Was sie spart | Womit |
|---|---|---|
| Seiten-Cache | den kompletten PHP-Durchlauf | Erweiterung oder Proxy |
| Objekt-Cache | wiederholte Datenbankabfragen | Redis/Valkey oder Memcached |
| Browser-Cache | erneutes Laden von Bildern, CSS | Header im Proxy |
Der Seiten-Cache bringt am meisten. Eine gecachte Seite kostet fast nichts, genau das rettet den Server, wenn ein Beitrag plötzlich Aufmerksamkeit bekommt.
# Bilder und Stylesheets lange vorhalten
location ~* \.(jpg|jpeg|png|webp|avif|svg|css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
Stelle 3: wp-cron entschärfen#
WordPress führt geplante Aufgaben aus, indem es sie bei einem Seitenaufruf anstößt. Zwei Folgen: Ohne Besucher passiert nichts, und bei vielen Besuchern passiert es zu oft.
// wp-config.php
define('DISABLE_WP_CRON', true);
# /etc/systemd/system/wp-cron.timer → alle 5 Minuten, unabhängig von Besuchern
[Timer]
OnCalendar=*:0/5
Persistent=true
# /etc/systemd/system/wp-cron.service
[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/wp cron event run --due-now --path=/var/www/html
Stelle 4: die Angriffsfläche, die wirklich benutzt wird#
Der WordPress-Kern ist gut gepflegt. Eingebrochen wird über Erweiterungen und Themes, meist über bekannte Lücken in Versionen, die seit Monaten ein Update hätten.
// wp-config.php
define('DISALLOW_FILE_EDIT', true); // kein Code-Editor im Adminbereich
define('WP_AUTO_UPDATE_CORE', 'minor'); // Sicherheitsupdates automatisch
# Hochgeladene Dateien dürfen niemals ausgeführt werden
location ~* /wp-content/uploads/.*\.php$ { deny all; }
# XML-RPC braucht fast niemand und wird ständig durchprobiert
location = /xmlrpc.php { deny all; }
Jede installierte Erweiterung ist Code mit vollem Datenbankzugriff, auch die deaktivierten, denn ihre Dateien liegen weiterhin erreichbar auf der Platte. Deaktiviert ist nicht entfernt. Was seit einem Jahr nicht gebraucht wurde, gehört gelöscht, nicht abgeschaltet.
# Bestandsaufnahme mit WP-CLI
wp plugin list --status=inactive
wp plugin list --update=available
wp core verify-checksums # wurden Kerndateien verändert?
Der letzte Befehl ist die schnellste Antwort auf die Frage „bin ich gehackt worden?“.
Sichern: zwei Teile, nicht einer#
WordPress besteht aus Datenbank (Beiträge, Einstellungen, Benutzer) und wp-content (Uploads, Themes, Erweiterungen). Fehlt eins, ist die Wiederherstellung unvollständig.
wp db export - | gzip > /backup/wp-db-$(date +%F).sql.gz
tar -C /var/www/html -czf /backup/wp-content-$(date +%F).tgz wp-content
Die Regeln dazu, mehrere Ziele, geprüfte Rückspielung, stehen in 3-2-1 und, für den Datenbankteil, in Datenbank sichern und wirklich zurückspielen.
Häufige Fragen#
Warum ist meine Seite langsam, obwohl der Server nichts tut?#
Meist wartet PHP auf die Datenbank oder auf einen externen Aufruf einer Erweiterung. Das langsame Abfrageprotokoll der Datenbank zeigt Ersteres (MariaDB-Fallen), die Netzwerkanalyse im Browser das Zweite.
Brauche ich einen Objekt-Cache?#
Ab dem Punkt, an dem angemeldete Nutzer (Redaktion, Shop-Kunden) am Seiten-Cache vorbeigehen, für sie greift nur der Objekt-Cache.
Automatische Updates für alles?#
Für den Kern und für Sicherheitsupdates ja. Für große Erweiterungen und das Theme lieber gestaffelt, mit Sicherung davor, ein automatisches Update, das die Seite zerlegt, passiert sonst nachts.
Reicht ein kleiner VPS?#
Für eine Seite mit überschaubarem Verkehr und aktivem Seiten-Cache ja. Der Speicher wird eher durch PHP-Prozesse als durch Besucher knapp, daher die Rechnung oben.
Sicherheits-Erweiterung installieren?#
Sie ersetzt keine Updates und keine Firewall, kann aber Anmeldeversuche begrenzen und Änderungen melden. Nützlich als Ergänzung, gefährlich als Beruhigung.
Kurz gesagt#
pm.max_childrenausrechnen, nicht schätzen, sonst kippt der Server unter Last.- Seiten-Cache bringt am meisten, Objekt-Cache für angemeldete Nutzer.
DISABLE_WP_CRONplus systemd-Timer: planbar statt besucherabhängig.- Eingebrochen wird über Erweiterungen. Ungenutzte löschen, nicht deaktivieren.
- Sicherung heißt Datenbank und
wp-content.
Geschrieben aus dem laufenden Betrieb: MRMedia betreibt eigene Proxmox- Hosts, einen Backup-Server und Kundendienste in der EU. Fehler in einer Anleitung sind Fehler im eigenen Betrieb. Korrekturen bitte über Discord.