WordPress-Skalierung für 10.000 Nutzer pro Stunde

WordPress-Skalierung für 10.000 Nutzer pro Stunde

Zuletzt überprüft: 12. Oktober 2026
13 Min. Lesezeit
Leitfaden
Core Web Vitals

Jeder Website-Betreiber träumt davon, “viral” zu gehen. Aber für einen Serveradministrator wird dieser Moment oft zum Albtraum. Die Seite lädt 30 Sekunden lang, und dann sehen die Nutzer nur noch “Error 500” oder “503 Service Unavailable”.

In diesem Artikel zeige ich Ihnen, wie Sie WordPress auf hohen Traffic vorbereiten und warum ein Cache-Plugin allein nicht reicht.


#Wie begrenzt das PHP-Worker-Limit den Traffic in WordPress

Die meisten WordPress-Abstürze entstehen nicht durch fehlenden RAM, sondern durch Erschöpfung des PHP Worker Limits.

#Was ist ein PHP Worker

Stellen Sie sich einen PHP Worker als Kassierer in einem Geschäft vor.

  • Wenn Sie 10 Kassierer haben (10 Worker).
  • Jeder Kunde (Besucher) braucht 1 Sekunde zur Bedienung (die Zeit, in der die Seite erzeugt wird).
  • Sie können maximal 10 Kunden pro Sekunde bedienen.

Wenn der 11. Kunde kommt, muss er warten. Wenn die Schlange voll ist, antwortet der Server mit Fehler 503.

In PHP-FPM ist dieser “Kassierer” ein Kindprozess des Pools. Die PHP-Dokumentation beschreibt pm.max_children direkt als Grenze für gleichzeitig bearbeitete Anfragen (php.net, FPM-Konfiguration). Anfragen über diese Grenze hinaus warten in der Socket-Warteschlange, deren Länge listen.backlog bestimmt (unter Linux standardmäßig 511).

#Wie berechnet man den Durchsatz der PHP Worker

Maximaler Traffic (RPS) = Anzahl Worker / durchschnittliche Generierungszeit der Seite

Rechenbeispiel mit 10 Workern auf einem Shared Hosting:

  • Die Seite wird in 0,5 s erzeugt, also schaffen Sie 20 Anfragen pro Sekunde.
  • Die Seite wird in 2,0 s erzeugt, also schaffen Sie nur 5 Anfragen pro Sekunde.

Dieselbe Formel zeigt, was die 10.000 Nutzer pro Stunde aus dem Titel bedeuten. Das sind im Schnitt knapp 3 Seitenaufrufe pro Sekunde, also eine Zahl, die bei 0,5 s pro Seite schon 10 Worker tragen. Schwierig wird es, wenn der Traffic nicht gleichmäßig verteilt ist, sondern in wenigen Minuten nach dem Teilen eines Links ankommt, oder wenn ein einziger Seitenaufruf mehrere PHP-Anfragen auslöst (AJAX, REST API, Warenkorb).

Fazit: Um zu skalieren, müssen Sie entweder mehr Kassierer einstellen (teurere VPS oder dedizierte Server) oder die Kassierer schneller arbeiten lassen (Code-Optimierung und Cache).


#Wie setzt man pm.max_children in PHP-FPM

Die Anzahl der Worker legen Sie in der Pool-Datei von PHP-FPM fest (meist www.conf). Die Direktive pm kennt drei Werte (php.net):

Modus pmFunktionsweiseWann sinnvoll
staticfeste Prozesszahl gleich pm.max_childrendedizierter Server mit vorhersehbarem Traffic
dynamicProzesszahl schwankt zwischen pm.min_spare_servers und pm.max_spare_servers, nie über pm.max_childrendie meisten VPS
ondemandProzesse starten erst bei einer Anfrage und enden nach pm.process_idle_timeout (Standard 10 s)viele kleine Pools auf einem Server

Im Modus dynamic ist pm.start_servers standardmäßig der Mittelwert aus min_spare_servers und max_spare_servers.

Die Obergrenze für pm.max_children bestimmt der Arbeitsspeicher. Rechenbeispiel: Der Server hat 8 GB RAM, MySQL, Redis und das System belegen 3 GB. Braucht ein PHP-FPM-Prozess mit WordPress und WooCommerce in der Spitze etwa 100 MB (messen Sie das auf Ihrem Server, zum Beispiel über die RSS-Spalte von ps), bleiben 5 GB für PHP, also rund 50 Prozesse. 200 Worker bringen in dieser Lage keinen Durchsatz, sondern schieben den Server bei der nächsten Lastspitze in den Swap.

Zwei Direktiven helfen bei Speicherlecks und hängenden Skripten:

  • pm.max_requests startet einen Prozess nach einer festen Zahl von Anfragen neu. Die PHP-Dokumentation nennt das als Workaround für Speicherlecks in Bibliotheken von Drittanbietern. Standard ist 0, also unbegrenzt.
  • request_terminate_timeout beendet einen Prozess, der eine einzelne Anfrage länger als die eingestellte Zeit bearbeitet, wenn max_execution_time das Skript aus irgendeinem Grund nicht stoppt. Standard ist 0, also aus.

#Wie erkennt man, ob PHP-FPM zu wenige Worker hat

Statt zu raten, aktivieren Sie die Statusseite des Pools mit pm.status_path (zum Beispiel /fpm-status) und geben sie in Nginx nur für Admin-Adressen frei. Die Felder, die die Dokumentation der FPM-Statusseite beschreibt, beantworten die Frage nach den Workern direkt:

  • listen queue: wie viele Anfragen gerade auf einen freien Prozess warten.
  • max listen queue: die größte Warteschlange seit dem Start des Pools.
  • active processes und idle processes: wie viele Prozesse arbeiten und wie viele warten.
  • max children reached: ein Wert von 1 oder mehr heißt, dass der Pool mindestens einmal an pm.max_children gestoßen ist.
  • slow requests: Anzahl der Anfragen, die request_slowlog_timeout überschritten haben.

Alle Zähler werden beim Neustart von FPM zurückgesetzt. Steigt max children reached und ist die Warteschlange nicht leer, fehlen Worker oder die Seiten werden zu langsam erzeugt. Um zu klären, was davon zutrifft, setzen Sie request_slowlog_timeout (zum Beispiel 5 s) und einen Pfad für slowlog. PHP schreibt dann für jede langsame Anfrage einen Backtrace, also den Namen des Plugins und der Funktion, die den Worker blockiert.


#Wie entlastet Full Page Cache die PHP-Worker

Das schnellste PHP ist das, das gar nicht ausgeführt wird.

Wenn Ihre Seite statisch ist (z. B. ein Blogartikel), sollte PHP sie nicht jedes Mal neu generieren. Sie sollten eine fertige HTML-Datei ausliefern.

#Arten von Full Page Cache

  1. Plugins: WP Rocket, W3 Total Cache. Sie legen HTML-Dateien auf der Festplatte ab. Für kleine Seiten genügt das, aber wenn die Anfrage trotzdem durch PHP läuft, ist der Worker trotz Cache belegt.
  2. Server-Side Cache (Pro):
    • Varnish: Ein Proxy-Server, der fertige Seiten vor Apache oder Nginx im RAM hält.
    • Nginx FastCGI Cache: In Nginx integriert.
    • Cloudflare: Seiten ausliefern, bevor sie Ihr Hosting erreichen.

Regel: Anonymer Traffic (nicht eingeloggte Besucher) sollte PHP gar nicht erreichen. Jeder Aufruf aus dem Cache ist ein freier Worker für Warenkorb, Kundenkonto und eingeloggte Nutzer.


#Wie konfiguriert man Varnish für WordPress und Cookies

Varnish cacht auf einer frisch eingerichteten WordPress-Seite oft fast nichts, und das entspricht der Dokumentation. Laut der Varnish-Anleitung zur Trefferquote speichert die Standardkonfiguration keine Antworten mit Set-Cookie-Header, und sobald der Client einen Cookie-Header sendet, umgeht Varnish den Cache und geht direkt zum Backend. Auf einer Seite mit Google Analytics oder einem Consent-Banner hat fast jeder Besucher irgendein Cookie.

Die Lösung aus der Dokumentation: Cookies in vcl_recv überall dort entfernen, wo das Backend sie nicht braucht. WordPress erkennt eingeloggte Nutzer an Cookies mit den im Core definierten Präfixen (wordpress_, wordpress_sec_, wordpress_logged_in_), Kommentierende an comment_author_. Diese müssen zu PHP durchgereicht werden, der Rest kann weg:

sub vcl_recv {
    if (req.url ~ "^/(wp-admin|wp-login\.php|warenkorb|kasse|mein-konto)") {
        return (pass);
    }
    if (req.http.Cookie !~ "(wordpress_|wp-postpass_|comment_author_|woocommerce_items_in_cart|wp_woocommerce_session_)") {
        unset req.http.Cookie;
    }
}

Die Warenkorb- und Kassenpfade im Beispiel sind Platzhalter, passen Sie sie an die Slugs Ihres Shops an. Neuere Varnish-Versionen teilen das eingebaute vcl_recv in kleinere Routinen auf, und die Beschreibung des Built-in VCL zeigt, dass sich die Cookie-Regel einzeln über vcl_req_cookie abschalten lässt. Das ist nur dann sinnvoll, wenn das Backend selbst immer angibt, was gecacht werden darf.

#Wie leert man den Varnish-Cache nach dem Veröffentlichen

Eine lange Lebensdauer der Objekte hebt die Trefferquote, aber nach dem Bearbeiten eines Beitrags muss der Leser die neue Fassung sehen. Die Varnish-Dokumentation zu Purge und Ban beschreibt zwei Mechanismen:

acl purge {
    "localhost";
    "192.168.55.0"/24;
}

sub vcl_recv {
    if (req.method == "PURGE") {
        if (!client.ip ~ purge) {
            return (synth(405, "Not allowed."));
        }
        return (purge);
    }
}
  • Purge entfernt ein Objekt samt allen Varianten, die sich aus dem Vary-Header ergeben. Das Cache-Plugin schickt eine PURGE-Anfrage an die URL des Beitrags. Die acl-Liste ist Pflicht, weil sonst jeder im Internet Ihren Cache leeren kann.
  • Ban wirkt wie ein Filter auf bereits gecachte Objekte, etwa “alles unter /kategorie/”. Bans, die nur obj.* prüfen, verarbeitet im Hintergrund ein Thread namens “ban lurker”. Die Dokumentation rät, req.* in Bans zu vermeiden, weil das Request-Objekt in diesem Thread nicht verfügbar ist.

#Wie entlastet Redis die WordPress-Datenbank

Was ist mit WooCommerce-Shops oder Kundenbereichen, die sich nicht vollständig als HTML cachen lassen? Dort muss PHP arbeiten.

WordPress führt bei jedem Aufruf Dutzende gleicher SQL-Abfragen aus: Optionen der Seite laden, Kategorie-IDs laden, Metadaten des Nutzers laden.

Redis (Object Cache) speichert die Ergebnisse von Datenbankabfragen im RAM. Statt MySQL zu fragen, fragt PHP Redis.

Wie viele Abfragen wegfallen, hängt von Theme und Plugins ab. Sie können es selbst prüfen: Query Monitor zeigt die Anzahl der SQL-Abfragen auf einer Seite vor und nach dem Einschalten von Redis.

Im Jahr 2026 WooCommerce ohne Redis zu betreiben, ist Selbstsabotage.


#Was ändert die Datei object-cache.php in WordPress

WordPress hat selbst einen Object Cache, nur keinen persistenten. Die Dokumentation der Klasse WP_Object_Cache sagt klar, dass der Cache Daten standardmäßig nur im Speicher und nur bis zum Ende der aktuellen Anfrage hält. Jeder Seitenaufruf beginnt mit leerem Cache und fragt die Datenbank neu.

Persistenz bringt ein Drop-in. Die Funktion wp_start_object_cache() lädt die Datei wp-content/object-cache.php, falls vorhanden, und ab dann sprechen wp_cache_get(), wp_cache_set() und wp_cache_delete() mit Redis statt mit einem Array im Prozessspeicher. Plugins wie Redis Object Cache tun im Kern genau eines: Sie kopieren diese Datei und geben ihr die Verbindungsdaten.

Drei Folgen, die man leicht übersieht:

  • Transients verlassen die Datenbank. Mit einem externen Object Cache schreiben set_transient() und delete_transient() über wp_cache_* statt in die Tabelle wp_options (set_transient). Wer Redis leert, löscht also auch alle Transients.
  • Nicht alles landet in Redis. Der Core markiert die Gruppen counts, plugins und theme_json über wp_cache_add_non_persistent_groups() als nicht persistent, sie leben weiter nur im Speicher einer Anfrage.
  • Wann WordPress selbst Redis vorschlägt. Seit Version 6.1 empfiehlt Site Health einen persistenten Object Cache auf jeder Multisite-Installation sowie dann, wenn die automatisch geladenen Optionen 500 Einträge oder 100.000 Bytes überschreiten oder eine der Tabellen für Kommentare, Optionen, Beiträge, Terms oder Nutzer mindestens 1000 Zeilen hat. Die Schwellen lassen sich über den Filter site_status_persistent_object_cache_thresholds ändern.

#Wie setzt man maxmemory und die Eviction-Policy in Redis

Redis ohne Speichergrenze wächst, bis der RAM ausgeht. Auf 64-Bit-Systemen ist maxmemory 0, also keine Grenze, der Standard (redis.io, Eviction). Auf einem Server, der den Speicher mit PHP-FPM und MySQL teilt, setzen Sie die Grenze ausdrücklich:

maxmemory 512mb
maxmemory-policy allkeys-lru

Die 512 MB sind ein Beispiel, keine Empfehlung, wählen Sie den Wert nach der Datenmenge. Wichtiger ist die Policy. Die Redis-Dokumentation nennt allkeys-lru eine gute Standardwahl, wenn ein Teil der Schlüssel viel häufiger gelesen wird als der Rest, und das ist das typische Bild eines WordPress-Caches. Die volatile-*-Policies entfernen nur Schlüssel mit gesetzter Ablaufzeit, und wenn es keine solchen gibt, verhalten sie sich wie noeviction: Redis liefert dann beim Schreiben Fehler. In WordPress hat wp_cache_set() standardmäßig $expire = 0, ein großer Teil der Schlüssel hat also womöglich kein TTL. volatile-lru endet bei so einem Cache mit Schreibfehlern statt mit dem Verdrängen alter Daten.

Ob der Cache arbeitet, zeigt INFO stats: Die Trefferquote ist keyspace_hits / (keyspace_hits + keyspace_misses). Ein hohes evicted_keys bei niedriger Trefferquote heißt, dass die Speichergrenze zu klein ist oder die Policy die falschen Schlüssel entfernt.


#Wie schließt man den WooCommerce-Warenkorb vom Cache aus

Die WooCommerce-Dokumentation zur Konfiguration von Cache-Plugins nennt drei Seiten, die dynamisch bleiben müssen, weil sie Daten des einzelnen Kunden zeigen: Warenkorb, Kasse und Mein Konto. Sie nennt auch die Cookies, bei denen der Cache zurücktreten muss: woocommerce_cart_hash und woocommerce_items_in_cart verfolgen Änderungen im Warenkorb, wp_woocommerce_session_ identifiziert den Warenkorb des Kunden. Wenn ein Datenbank-Cache auf dem Server auch Sessions erfasst, muss _wc_session_ davon ausgenommen werden.

Dieselbe Dokumentation warnt, dass manche Hoster mit serverseitigem Cache die Seite Mein Konto cachen, was zu einer Schleife beim Zurücksetzen des Passworts führt. Das ist das erste Symptom, das Sie prüfen sollten, wenn Kunden nach dem Einschalten von Varnish melden, dass sich das Passwort nicht ändern lässt.


#Wie konfiguriert man Nginx FastCGI Cache statt Varnish

Wenn auf dem Server ohnehin Nginx läuft, ist ein separater Varnish nicht nötig. Das Modul ngx_http_fastcgi_module cacht Antworten von PHP-FPM auf der Festplatte und hält die Schlüssel im gemeinsamen Speicher. Die Nginx-Dokumentation gibt an, dass eine Zone von 1 MB etwa 8.000 Schlüssel fasst.

fastcgi_cache_path /var/cache/nginx/wp levels=1:2 keys_zone=wp:10m inactive=60m;

server {
    set $skip_cache 0;
    if ($http_cookie ~* "wordpress_logged_in_|wp-postpass_|comment_author_|woocommerce_items_in_cart|wp_woocommerce_session_") {
        set $skip_cache 1;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php-fpm.sock;
        include fastcgi.conf;
        fastcgi_cache wp;
        fastcgi_cache_key "$scheme$request_method$host$request_uri";
        fastcgi_cache_valid 200 301 302 10m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        fastcgi_cache_lock on;
        fastcgi_cache_use_stale updating error timeout;
        fastcgi_cache_background_update on;
    }
}

Was die wichtigsten Direktiven laut Nginx-Dokumentation tun:

  • fastcgi_cache_bypass nimmt die Antwort nicht aus dem Cache, fastcgi_no_cache speichert sie nicht, sobald einer der Werte nicht leer und ungleich “0” ist. Deshalb bekommen im Beispiel beide dieselbe Variable.
  • fastcgi_cache_lock on lässt nur eine Anfrage zu PHP durch, die einen neuen Cache-Eintrag füllt. Die anderen warten. Genau das schützt die Worker, wenn nach dem Teilen eines Links Hunderte Menschen dieselbe, noch nicht gecachte Adresse öffnen.
  • fastcgi_cache_use_stale updating zusammen mit fastcgi_cache_background_update on liefert dem Leser eine ältere Kopie und aktualisiert im Hintergrund.
  • Der Parameter inactive entfernt Einträge, die in der angegebenen Zeit niemand gelesen hat, unabhängig von ihrer Frische. Standard sind 10 Minuten.

#Warum stürzte WordPress am Black Friday ab

Projekt: Nachrichtenportal am Black Friday. Plötzlicher Traffic-Anstieg, der Server stürzte in 3 Minuten ab, CPU bei 100 %.

#Ursachen des Absturzes

  1. Killer-Ajax: Ein “Besucherzähler”-Plugin sendete bei jedem Besuch einen AJAX-Request (admin-ajax.php), der nicht gecacht wurde.
    • Ajax in WordPress startet immer die ganze PHP-Engine mit allen Plugins. Die Datei admin-ajax.php lädt wp-load.php und danach noch die Dateien des Admin-Bereichs.
    • 10.000 Besuche bedeuteten 10.000 vollständige WordPress-Starts. Das Worker-Limit war in 10 Sekunden erschöpft.
  2. Keine Datenbank-Indexe: Die Beitragstabelle hatte 500.000 Einträge, ein Plugin für “Ähnliche Beiträge” nutzte ORDER BY RAND(). Das tötete MySQL.

#Wie der Absturz behoben wurde

  1. Ajax deaktiviert (Google Analytics genutzt, also JavaScript im Browser).
  2. Redis implementiert, was die MySQL-Last um 90 % senkte.
  3. Varnish konfiguriert, um die Startseite aus dem RAM auszuliefern.

Ergebnis in diesem Fall: Der Server, der bei 100 Nutzern einbrach, bediente danach 5000 gleichzeitig bei 5 % CPU. Das sind Zahlen aus einem einzigen Portal, keine Norm für jede Installation.


#Wie überwacht man die WordPress-Performance

  1. Query Monitor: Plugin für SQL-Analyse und Speicherverbrauch der aktuellen Seite.
  2. New Relic: Professionelles APM-Monitoring, das zeigt, welche PHP-Funktion oder welches Plugin die meiste Zeit kostet.
  3. PHP-FPM-Status: pm.status_path und Slowlog, wie oben beschrieben.
  4. Redis: INFO stats und INFO memory.
  5. Logs: tail -f im SSH (siehe unseren SSH-Leitfaden).

#Was brauchen Sie für WordPress unter Last

  1. Hosting: Cloud VPS mit Redis und konfigurierbaren PHP Workern.
  2. Worker: pm.max_children aus dem RAM berechnen und pm.status_path aktivieren.
  3. Cache: Varnish oder Nginx FastCGI, mit einer Liste der Cookies, die den Cache umgehen, und einer ACL für PURGE.
  4. Datenbank: Redis als Object Cache, mit maxmemory und allkeys-lru.
  5. Shop: Warenkorb, Kasse und Mein Konto vom Cache ausschließen.
  6. Bereinigung: Kein Admin-Ajax im Frontend.
  7. CDN: Bilder und CSS zu Cloudflare verlagern.

Ein stabiles WordPress ist keine Magie, sondern Rechnen. Senken Sie die Generierungszeit (Cache) und erhöhen Sie den Durchsatz (Worker und Redis).

Nächster Schritt

Machen Sie aus dem Artikel eine echte Umsetzung

Dieser Block stärkt die interne Verlinkung und führt Nutzer gezielt zum nächsten sinnvollen Schritt im Service- und Content-System.

Soll das Thema auf Ihrer Website umgesetzt werden?

Wenn Core Web Vitals, Rendering oder WordPress-Overhead das Problem sind, setze ich einen klaren Optimierungsplan auf und implementiere ihn.

Artikel-FAQ

Häufig gestellte Fragen

Praktische Antworten zur Umsetzung des Themas.

SEO-readyGEO-readyAEO-ready1 Q&A
Was ist WordPress-Skalierung?#
Dabei geht es darum, WordPress für hohe Last stabil zu halten, etwa mit Redis, Varnish und einem sauberen Verständnis von PHP Workers.

Sie brauchen ein FAQ für Branche und Zielmarkt? Wir erstellen eine Version passend zu Ihren Business-Zielen.

Kontakt aufnehmen

Ähnliche Artikel