Von Christian Wilms · 2. Juni 2026 · Werkstatt

WooCommerce-Shop lädt 8 Sekunden: die echten Ursachen

Ein Klick auf die Kategorieseite, und dann: nichts. Der Tab lädt, der Spinner dreht, nach acht Sekunden steht endlich die Seite. Als Betreiber gewöhnt man sich daran, weil man den eigenen Shop täglich sieht. Kunden gewöhnen sich nicht. Sie brechen ab, kaufen woanders und kommen nicht zurück. Ein langsamer Shop ist kein Schönheitsfehler, sondern verliert bei jedem einzelnen Seitenaufruf Geld, im Zweifel an den Mitbewerber, dessen Seite in unter einer Sekunde steht.

Der Standard-Reflex lautet: Caching-Plugin installieren. Das ist selten falsch, löst das Problem aber oft nicht. Ein Page-Cache greift bei einem Shop genau dort nicht, wo es weh tut: Warenkorb, Checkout, Mein-Konto, gefilterte Kategorieseiten, eingeloggte Kunden. Diese Requests laufen ungebremst durch den kompletten PHP- und Datenbank-Stack. Ist der langsam, bleibt der Shop langsam, egal wie viele Optimierungs-Plugins zusätzlich laufen.

Erst messen: TTFB oder Frontend?

Bevor irgendetwas optimiert wird, muss klar sein, wo die Zeit verloren geht. Die wichtigste Trennlinie verläuft zwischen Time to First Byte (TTFB) und Frontend-Ladezeit.

curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s | Gesamt: %{time_total}s\n" \
  https://shop.example.com/produkt-kategorie/

Liegt der TTFB über einer Sekunde, arbeitet der Server zu lange an der Antwort: PHP, Datenbank, externe API-Calls. Liegt der TTFB bei 200 bis 400 Millisekunden und die Seite fühlt sich trotzdem zäh an, liegt das Problem im Frontend: unkomprimierte Bilder, zu viel JavaScript, Render-Blocking. Das sind zwei völlig verschiedene Baustellen mit verschiedenen Werkzeugen. Bei einem 8-Sekunden-Shop ist es in der Praxis fast immer der TTFB, und genau dort helfen die üblichen Frontend-Tipps nicht weiter.

Wichtig: einmal als Gast messen und einmal eingeloggt oder mit Warenkorb-Cookie. Die zweite Messung zeigt den Wert, den ein Page-Cache niemals verbessern wird.

Ursache 1: wp-cron läuft im Request deiner Kunden

WordPress hat keinen echten Scheduler. Geplante Aufgaben werden huckepack im Seitenaufruf eines zufälligen Besuchers ausgeführt. Bei einem Blog fällt das kaum auf. WooCommerce dagegen plant permanent Aufgaben über den Action Scheduler: Bestands-Synchronisation, Webhooks, E-Mails, Sitzungsbereinigung, Tracking-Pings von Erweiterungen. Hat sich dort eine Warteschlange aufgestaut, zieht genau der Kunde die Niete, der gerade in den Checkout will: Sein Request arbeitet erst die Cron-Jobs ab und liefert dann die Seite aus.

Die Lösung ist seit Jahren dieselbe und wird trotzdem ständig ausgelassen: internen Cron abschalten und einen echten System-Cron einrichten.

// wp-config.php
define('DISABLE_WP_CRON', true);
# Crontab des Webusers: alle 5 Minuten
*/5 * * * * cd /var/www/shop && wp cron event run --due-now >/dev/null 2>&1

In Shops mit Action-Scheduler-Stau habe ich allein durch diesen Schritt TTFB-Werte von mehreren Sekunden auf einen Bruchteil fallen sehen. Kostet nichts, dauert zehn Minuten.

Ursache 2: Autoload-Ballast in wp_options

Die Tabelle wp_options enthält Einträge, die als Autoload markiert sind, klassisch mit yes, seit WordPress 6.6 auch mit on oder auto-on. Diese Einträge werden bei jedem einzelnen Request komplett geladen, ob die Seite sie braucht oder nicht. Über die Jahre lagern Plugins dort Logs, Transients, Sessions und ganze Konfigurations-Blobs ab. Auch längst deinstallierte Plugins räumen ihre Daten fast nie weg.

wp db query "SELECT ROUND(SUM(LENGTH(option_value))/1024) AS autoload_kb \
  FROM wp_options WHERE autoload IN ('yes','on','auto-on','auto');"

wp db query "SELECT option_name, ROUND(LENGTH(option_value)/1024) AS kb \
  FROM wp_options WHERE autoload IN ('yes','on','auto-on','auto') \
  ORDER BY LENGTH(option_value) DESC LIMIT 20;"

Alles über einem Megabyte ist ein Warnsignal. Ich habe Shops gesehen, die bei jedem Seitenaufruf mehrere Megabyte Options-Daten aus der Datenbank ziehen, darunter kilobyteweise verwaiste Transients eines Plugins, das seit zwei Jahren nicht mehr installiert war. Die Top-20-Liste zeigt sofort die Kandidaten: verwaiste Einträge löschen, große und selten gebrauchte Einträge per wp option set-autoload <name> off vom Autoload ausnehmen. Vorher Backup, versteht sich.

Ursache 3: Ungecachte Filter- und Produkt-Queries

Kategorieseiten mit Filtern für Preis, Marke oder Verfügbarkeit sind die teuersten Seiten im Shop. Jeder Filter übersetzt sich in meta_query-Bedingungen, und die landen als JOINs auf wp_postmeta. Bei 200 Produkten ist das egal. Bei einem Händler mit 20.000 Produkten und je 40 Metafeldern liegen dort schon 800.000 Zeilen, dazu kommen Varianten, Bildzuordnungen und ohne HPOS sämtliche Bestelldaten. So wächst die Tabelle in die Millionen, und pro Filteraufruf wird sie mehrfach gejoint. Sortierung nach Beliebtheit oder Preis macht es schlimmer.

Drei Hebel: Erstens prüfen, ob die WooCommerce-Lookup-Tabellen (wp_wc_product_meta_lookup) gefüllt sind und genutzt werden, neu aufbauen lassen sie sich unter WooCommerce > Status > Werkzeuge. Zweitens Bestellungen auf HPOS (High-Performance Order Storage) migrieren, das entlastet postmeta massiv. Drittens, und das ist der eigentliche Punkt: Ohne persistenten Object-Cache rechnet der Server dieselbe teure Filter-Query bei jedem Aufruf komplett neu.

Ursache 4: Der Plugin-Stapel

40, 50, 60 aktive Plugins sind in gewachsenen Shops normal. Das Problem ist nicht die Anzahl an sich, sondern was die Plugins pro Request tun. Ein Security-Scanner, der jeden Aufruf analysiert. Ein Abandoned-Cart-Tool, das bei jedem Seitenaufruf einen externen HTTP-Call absetzt. Zwei SEO-Plugins parallel. Ein Page-Builder, der auch auf Seiten lädt, die er gar nicht baut.

Raten hilft hier nicht, messen schon:

# einmalig: wp package install wp-cli/profile-command
wp profile stage --all --orderby=time
wp profile hook init --orderby=time --spotlight

Das WP-CLI-Paket wp profile zeigt pro Ladephase und pro Hook, wo die Zeit bleibt. Wenn ein einzelnes Plugin in init mehrere hundert Millisekunden verbrennt oder ein externer Call mit zwei Sekunden Timeout auftaucht, steht der Schuldige schwarz auf weiß im Terminal. Das ist etwas anderes, als im Blindflug Plugins zu deaktivieren und zu hoffen.

Ursache 5: Kein Object-Cache

Der unterschätzteste Hebel überhaupt. WordPress hält Datenbank-Ergebnisse intern im Object-Cache vor, aber ohne persistentes Backend lebt dieser Cache genau einen Request lang. Jeder Seitenaufruf baut alles neu auf: Options, Terms, Produktdaten, Sessions.

Ein Redis-Server plus ein Drop-in wie Redis Object Cache ändert das grundlegend. Gerade WooCommerce profitiert, weil Sessions, Warenkorb-Daten und die vielen kleinen wiederkehrenden Queries dann aus dem RAM kommen statt aus MariaDB. Auf einem eigenen vServer ist Redis in einer halben Stunde eingerichtet, viele Managed-Hoster bieten es als Schalter im Panel an. Wer einen Shop mit eingeloggten Kunden ohne Object-Cache betreibt, verschenkt die wirksamste serverseitige Optimierung.

Der Diagnose-Weg in 30 Minuten

  1. TTFB messen mit curl, als Gast und mit Warenkorb. Erst damit ist klar, ob Server oder Frontend das Problem ist.
  2. Server-Timing aktivieren: Tools wie Query Monitor liefern Server-Timing-Header, die im Netzwerk-Tab der Browser-DevTools pro Request zeigen, wie viel Zeit auf Datenbank und PHP entfällt.
  3. wp profile stage --all ausführen: Welche Phase frisst die Zeit? Bootstrap (Plugins und Options), Main Query oder Template?
  4. Autoload-Größe prüfen mit den SQL-Queries oben.
  5. Cron prüfen: wp cron event list zeigt überfällige Events. Stauen die sich, System-Cron einrichten.
  6. Nach jedem Fix neu messen. Eine Änderung, eine Messung. Sonst weiß am Ende niemand, was tatsächlich geholfen hat.

Fazit: Erst die Ursache, dann das Cache-Plugin

Die acht Sekunden kommen fast nie von einer einzelnen Einstellung, sondern vom Stapel: Cron im Request-Pfad, mehrere Megabyte Autoload, ungecachte Filter-Queries, ein gesprächiges Plugin und kein Redis. Ein Caching-Plugin obendrauf kaschiert das für anonyme Besucher und lässt zahlende Kunden im Checkout weiter warten.

Die Reihenfolge, die sich in der Praxis bewährt: messen, Cron auslagern, Autoload entrümpeln, Plugin-Stapel mit wp profile durchleuchten, Object-Cache aktivieren, und erst dann der Page-Cache als Schlussstein für Gäste. Nimm dir die 30 Minuten für den Diagnose-Weg oben. Allein zu wissen, ob dein Problem TTFB oder Frontend heißt, erspart dir wochenlange Optimierung an der falschen Stelle. Und wenn die Messwerte auf dem Tisch liegen und es trotzdem hakt: Mit diesen Zahlen kann jeder Entwickler sofort gezielt weiterarbeiten.