Navigation überspringen

WordPress schneller machen, Teil 1: Caching

29.3.2017·Kommentare:  0 · ~ 6 min

Anfang des Jahres habe ich benedikt.io mit zwei Maßnahmen wesentlich schneller gemacht: durch Caching und mit der Minimierung auf nur einen Request. Wie damals angekündigt, hier der erste Teil meiner Erfahrungen als Tutorial, in dem ich erkläre …

Caching: Wie funktioniert’s, was bringt’s?

WordPress-Caching-Plugins funktionieren prinzipiell immer gleich: Alle Seiten und Posts einer WordPress-Installation werden als statische Files in einem eigenen Cache-Ordner abgelegt. Wird eine Seite aufgerufen, wird statt der dynamisch generierten PHP-Seite die bereits fertige HTML-Seite ausgeliefert.

Das hat zwei Vorteile: Einerseits sinkt die Serverlast, weil eine angeforderte Seite nicht mehr bei jedem Request mit PHP und über MySQL-Abfragen generiert werden muss, andererseits kann der Server diese schneller ausliefern. Für den User lädt die Seite im Browser somit so gut wie »instant« – und das auch bei günstigen Webhostern.

Die Sache hat natürlich einen Haken: Seiten, die dynamischen Inhalt wie die neuesten Kommentare anzeigen, müssen regelmäßig neu erstellt werden, um keine alten Daten anzuzeigen – doch dazu später mehr.

Wer wissen will, wie viele Millisekunden bei der Auslieferung potentiell eingespart werden können, braucht nur eine HTML-Seite (z.B. eine statische Kopie eines Blogposts) auf seinen Webspace zu laden und die Ladezeiten mittels Webdeveloper-Tools (Cache deaktivieren!) mit jenen von WordPress zu vergleichen. Als Richtwert sollten damit so viele Millisekunden eingespart werden können, wie im WordPress-Footer mit der Funktion timer_stop(); ausgegeben werden (je nach Webhoster ca. 700 ms).

Die Qual der Wahl: »W3 Total Cache« oder »WP Super Cache«?

Für WordPress gibt eine unüberschaubare Anzahl an Caching-Plugins. Ich habe mit W3 Total Cache und WP Super Cache zwei der populäreren ausprobiert und mich für letzteres entschieden. Das hat aber weniger mit der Performance oder Plugin-Qualität zu tun, sondern einfach damit, dass WP Super Cache im Gegensatz zu W3 Total Cache sofort funktioniert hat und von der Bedienung her zugänglicher ist. Wer mehr wissen will, findet auf Dashboard Junkie einen guten Vergleich der beiden Plugins.

WP Super Cache richtig konfigurieren

Da Caching-Plugins meistens auf Seiten mit viel Traffic ausgelegt sind, ist für Seiten mit zwei- bis dreistelligen Besucherzahlen pro Tag die richtige Konfiguration entscheidend. Denn standardmäßig cacht das Plugin Seiten nur wenige Minuten lang und spielt, wegen der Kommentarfunktion, Seiten für bereits bekannte User ungecacht aus. Das ist für große Seiten ein guter Kompromiss aus Performance und Aktualität, bringt für kleine Seiten aber wenig bis gar nichts. Wichtig ist, dass man die Preload-Funktion aktiviert, damit die komplette Website vorab im Cache bereitgestellt wird und nicht nur auf Requestbasis gecachte Seiten erstellt werden. Dafür empfehle ich, nachdem man das Plugin installiert hat, im WordPress-Adminbereich unter Einstellungen → WP Super Cache folgende Einstellungen zu aktivieren (und nicht aufgelistete Optionen entsprechend zu deaktivieren):

Reiter »Easy«

Reiter »Erweitert«

Reiter »Preload«

PHP oder mod_rewrite für Auslieferung nutzen?

Eine Anmerkung zur Auslieferungs-Einstellung im Reiter »Erweitert«: Hier kann man bei Bedarf die PHP-Engine komplett umgehen indem in der .htaccess-Datei entsprechende Rewrite-Regeln hinzufügt werden, welche direkt auf die statischen Files verweisen. Bei sehr (sehr, sehr) viel Traffic kann das tatsächlich einen Unterschied machen, bei wenigen Besuchern eher nicht (siehe dazu auch Dashboard-Junkie-Vergleich). Diese Einstellung hat bei mir auch bei W3 Total Cache zu besagtem Problem geführt. Die verbirgt sich dort nämlich hinter der etwas unklar beschrifteten Option »disk:enhanced« (»disk:basic« entspricht dem PHP-Modus) und kann in Zusammenhang mit Nginx zu Problemen führen. Ich erwähne das, weil mich die Fehlersuche diesbezüglich einiges an Zeit gekostet hat. Prinzipiell spricht aber nichts gegen W3 Total Cache, welches man natürlich genau so verwenden kann.

Probleme beim Caching der Homepage

Ich hatte am Anfang das Problem, dass die Homepage trotz diverser Einstellungen nur wenige Minuten lang gecacht wurde – hält man sich genau an die oben beschriebenen Einstellungen wird aber die Homepage permanent gecacht (die damit verbundenen Probleme sowie Lösungsansätze findet ihr weiter unten).

WordPress-Kommentare und Caching

Sobald ein User einen Kommentar abgibt, gilt dieser für WP Super Cache als bekannter User und erhält fortan gar keine gecachten Seiten mehr. Ich vermute das hängt damit zusammen, dass nur so ein User nach dem Absenden eines Kommentars eine Erfolgsmeldung von WordPress bekommen oder direkt zu seinem Kommentar mit entsprechender Moderationsmeldung geleitet werden kann. Allerdings bestraft man so seine aktivsten User mit einer langsameren Seite – das war für mich keine Alternative.

Mit den oben angegebenen Einstellungen wird dieses zwar Problem behoben, allerdings springt dann beim Klick auf »Absenden« die Seite einfach nach oben und der User erhält keinerlei Feedback darüber, ob der Kommentar erfolgreich gesendet wurde. Ich habe dieses Problem gelöst, indem Kommentare per Ajax gesendet werden und entsprechend Feedback ausgegeben wird. Will man die Seite später in einem einzigen Request ausliefern, empfiehlt es sich außerdem auf jQuery zu verzichten. Das ist freilich nur sinnvoll, wenn man nicht viel JavaScript-Funktionalität benötigt. Ist das der Fall kann man mit folgendem Code1 Kommentare asynchron absetzen:

function AJAXPost(formId) {
var elem = document.getElementById(formId).elements;
var url = document.getElementById(formId).action;
var params = "";
var value;
for (var i = 0; i < elem.length; i++) {
if (elem[i].tagName == "SELECT") {
value = elem[i].options[elem[i].selectedIndex].value;
} else {
value = elem[i].value;
}
params += elem[i].name + "=" + encodeURIComponent(value) + "&";
}
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
} else {
// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("POST",url,false);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(params);
var node = document.createElement("p");
var textnode = document.createTextNode("Vielen Dank!
Dein Kommentar erscheint in Kürze.");
node.appendChild(textnode);
node.classList.add('update');
document.getElementById("commentForm").appendChild(node);
document.getElementById("commentForm").reset();
return xmlhttp.responseText;
}

In den form-Tag des Kommentarformulars fügt man zudem …

onsubmit="AJAXPost('[id]');return false;"

… ein, um die Funktion beim Absenden aufzurufen.

Man kann diesen Code natürlich noch wesentlich verfeinern, indem man z.B. die Antwort-Codes überprüft (z.B. wenn jemand den gleichen Kommentar mehrmals senden möchte) oder eine »Senden«-Animation einblendet.

Cache like a Pro – mit Cronjobs

Wie zuvor erwähnt, funktionierte das dauerhafte Caching der Homepage nicht auf Anhieb. Hat man diese Hürde bewältigt steht man vor dem Problem, dass sich die Homepage (bzw. andere Seiten abseits von normalen Posts) genau im richtigen Moment aktualisieren soll. Man könnte natürlich die entsprechende Seite im WordPress-Adminbereich öffnen und einfach neu speichern (dies löst eine Cache-Aktualisierung aus) – auf Dauer ist das aber keine praktikable Lösung.

Viel einfacher ist es, WP Super Cache durch Löschen der generierten Seite im Cache und erneutem Aufruf der »echten« Seite eine aktuelle gecachte Version erstellen zu lassen. Und das funktioniert am besten mit einem Cronjob, der diese Aufgabe in regelmäßigen Abständen automatisch erledigt. Leider bieten nicht alle günstigen Hosting-Anbieter kostenlose Cronjobs an – und wenn, dann vielleicht nur mit einer Ausführung pro Tag.

Man kann Cronjobs allerdings auch sehr einfach selbst hosten und ausführen, indem man diese z.B. auf einem Raspberry Pi laufen lässt. Zuerst schreibt man ein einfaches PHP-Skript, das die jeweiligen Files (einmal die HTML- und einmal die gzip-Version) aus dem Cache löscht und die entsprechende URL erneut aufruft (Beispiel für die Homepage):

$cached="./cache/supercache/[domain.tld]/index-https.html";
$cached_gz="./cache/supercache/[domain.tld]/index-https.html.gz";
if (file_exists($cached)) {
unlink($cached);
}
if (file_exists($cached_gz)) {
unlink($cached_gz);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://[domain.tld]/[page]");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
curl_close($ch);

Cronjobs auf einem Raspberry Pi

In Raspbian kann man auf der Kommandozeile zunächst mit2

sudo apt-get install gnome-schedule

… eine grafische Benutzeroberfläche für Crontab installieren (ja, ich mag GUIs), welche fortan unter Menü → Systemwerkzeuge → Geplante Aufgaben aufgerufen werden kann.

Dort kann man anschließend über das Feld »Befehl«3

/usr/bin/wget -q -O temp.txt https://[domain.tld]/[update-script-file].php

… den Aufruf des Skripts eintragen. Bei »Zeit und Datum« trägt man unter »Erweitert« im Feld »Minute« beispielsweise */5 ein (in die restlichen Felder ebenfalls »*« eintragen), um den Job alle 5 Minuten laufen zu lassen.

Voilà!

Schon habt ihr eine superschnelle WordPress-Website, die weiterhin eine komfortable Kommentarfunktion bietet und dank automatischer Cache-Invalidation mit Cronjobs immer aktuell ist.

Da geht noch mehr

Man kann die Seitenauslieferung wie in der Einführung angekündigt noch weiter optimieren, indem man sämtliche Requests bis auf einen eliminiert. Wie man das am Besten in WordPress macht erfahrt ihr demnächst im zweiten Teil dieses Tutorials.

Über Feedback, Verbesserungsvorschläge und weitere Tipps zum Thema Caching freue ich mich in den Kommentaren.


  1. Quelle: Stack Overflow: »Simple ajax form using javascript no jQuery« 
  2. Quelle: raspberrypi.org: »Scheduling Tasks with Cron« 
  3. Quelle: The Geek Stuff: »How to Execute PHP Script Using Crontab in Linux« 

Neueste Artikel

Schlagwörter

· · · ·


Kommentieren

Dieser Eintrag kann nicht mehr kommentiert werden.