Zum Inhalt springen
myvpsguide

WordPress auf dem eigenen Server: schnell, sicher, wartbar

PHP-FPM richtig dimensionieren, Zwischenspeicher an die richtige Stelle legen, wp-cron entschärfen und die Angriffsfläche schließen, die tatsächlich benutzt wird, die Erweiterungen.

veröffentlicht 19.08.2026
3 min lesezeit
fortgeschritten

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:

EbeneWas sie spartWomit
Seiten-Cacheden kompletten PHP-DurchlaufErweiterung oder Proxy
Objekt-Cachewiederholte DatenbankabfragenRedis/Valkey oder Memcached
Browser-Cacheerneutes Laden von Bildern, CSSHeader 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; }
Die drei Erweiterungen, die niemand vermisst

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_children ausrechnen, nicht schätzen, sonst kippt der Server unter Last.
  • Seiten-Cache bringt am meisten, Objekt-Cache für angemeldete Nutzer.
  • DISABLE_WP_CRON plus systemd-Timer: planbar statt besucherabhängig.
  • Eingebrochen wird über Erweiterungen. Ungenutzte löschen, nicht deaktivieren.
  • Sicherung heißt Datenbank und wp-content.
MRMedia

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.