Eine langsame WordPress-Seite beschleunigen: meine Reihenfolge
"Unsere WordPress-Seite ist langsam geworden" ist eine der häufigsten Anfragen, die ich bekomme, und der erste Impuls ist meist, mit einem Plugin anzufangen, einem Bildoptimierer, einem "Speed"-Plugin, das verspricht, alles zu lösen. Manchmal hilft das ein wenig. Aber die größten Verbesserungen kommen fast immer von Dingen, die mit WordPress' Plugin-Ökosystem gar nichts zu tun haben, und sie in der falschen Reihenfolge anzugehen bedeutet, Zeit in eine 10%-Verbesserung zu stecken, bevor das behoben ist, was für die anderen 70% verantwortlich ist.
Zuerst: liegt es am Server oder an der Seite?
Bevor ich irgendetwas anfasse, prüfe ich die Time-to-First-Byte, also wie lange der Server braucht, um mit dem Senden einer Antwort zu beginnen, unabhängig davon, wie lange der Browser danach braucht, um alles zu laden:
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\n" https://example.com
Eine TTFB über einer halben Sekunde auf einer ausgeloggten Seite bedeutet meist, dass WordPress bei jeder Anfrage echte Arbeit verrichtet, die Datenbank wird abgefragt, PHP läuft, nichts ist gecacht. Eine schnelle TTFB, aber ein langsamer Gesamt-Pageload deutet auf das Frontend hin, Bilder, Skripte, Fonts, was ein anderes (und meist einfacheres) Problem ist.
Wenn es der Server ist: Caching, in Schichten
Eine WordPress-Seite, die für jeden Besucher identisch ist (was auf die meisten Seiten der meisten Websites zutrifft), braucht PHP und MySQL nicht bei jeder Anfrage. Ein Page-Cache, sei es über ein Plugin wie WP Super Cache oder besser auf Serverebene mit Nginx' fcgicache oder einem ähnlichen Reverse-Proxy-Cache, liefert eine statische Kopie aus und überspringt PHP für gecachte Anfragen komplett. Diese einzige Änderung macht oft den Unterschied zwischen einer TTFB von 800ms und 50ms, denn es ist der Unterschied zwischen "WordPress ausführen" und "eine Datei ausliefern".
Für die Anfragen, die tatsächlich PHP brauchen, eingeloggte Nutzer, alles Dynamische, cacht ein Object Cache (Redis oder Memcached) die Ergebnisse von WordPress' Datenbankabfragen, sodass wiederholte Lookups (von denen WordPress sehr viele macht, oft dieselbe Query Dutzende Male pro Seite) auf den Speicher statt auf MySQL treffen:
// wp-config.php, nach der Installation eines Redis-Object-Cache-Plugins
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_CACHE', true);
Und PHPs OPcache, der kompilierten PHP-Bytecode cacht, sodass dieselbe Datei nicht bei jeder Anfrage neu kompiliert wird, sollte einfach in php.ini aktiviert werden, dafür gibt es überhaupt keine WordPress-spezifische Konfiguration, und es profitiert jede PHP-Anfrage auf dem Server davon.
Die Datenbank sammelt Gewicht an
WordPress' Datenbank wächst auf Arten, die erst auffallen, wenn sie es tun: Beitragsrevisionen (jedes Autosave erzeugt eine neue Zeile), abgelaufene Transients (temporäre gecachte Daten, die oft nie aufgeräumt werden), und verwaiste Metadaten von entfernten Plugins. Auf einer kleinen Seite ist nichts davon dramatisch, aber auf einer Seite, die seit Jahren läuft, findet sich häufig eine wp_options-Tabelle, in der die autoload-Optionen (die bei jeder Seitenanfrage geladen werden, egal ob sie gebraucht werden) auf mehrere Megabyte summieren:
SELECT option_name, LENGTH(option_value) AS size
FROM wp_options
WHERE autoload = 'yes'
ORDER BY size DESC LIMIT 10;
Alles, was hier groß ist und von einem nicht mehr aktiven Plugin stammt, ist reiner Overhead bei jeder einzelnen Anfrage. Das aufzuräumen erfordert kein Raten, die Query zeigt genau, wo das Gewicht sitzt.
Dann, und erst dann, das Frontend
Sobald der Server schnell antwortet, hat Frontend-Optimierung, Lazy-Loading für Bilder, moderne Bildformate, das Verzögern unkritischer Skripte, tatsächlich ein Fundament, auf dem sie aufbauen kann. Das zuerst auf einer Seite mit 800ms TTFB zu tun bedeutet, die sichtbaren 20% zu polieren, während die unsichtbaren 80% (die Zeit, bevor der Browser überhaupt mit dem Rendern beginnt) unberührt bleiben.
Warum die Reihenfolge zählt
Jeder dieser Schritte ist für sich genommen nützlich, aber die Reihenfolge spiegelt wider, wo die Zeit auf einer typischen langsamen WordPress-Seite tatsächlich verloren geht: serverseitiges Caching adressiert den größten Brocken für ausgeloggten Traffic, Object Cache und OPcache adressieren die Kosten von WordPress selbst für alles andere, Datenbank-Cleanup entfernt angesammeltes Gewicht, das jede einzelne Seite betrifft, und Frontend-Optimierung poliert, was übrig bleibt. Mit dem Ende dieser Liste anzufangen, weil es am sichtbarsten ist, ein Bild, ein Skript, fühlt sich produktiv an, optimiert aber zuerst das kleinste Stück der Gesamtzeit.