Vor einiger Zeit hatte ich mal mit der Beschleunigung dieses Blogs (und der anderen wazong-Seiten) beschäftigt. Dabei hatte ich mit Varnish schnell mittelgute Ergebnisse bekommen, es waren aber einige Fragen offengeblieben. Für einen Teil habe ich inzwischen Lösungen gefunden, bei anderen bastle ich noch.
Da war zuerst das access.log, in dem nur noch Zugriffe von 127.0.0.1 verzeichnet wurden. Dafür gibt es ein Apache-Modul, das den verbindenden Client durch den Inhalt des „X-Forwarded-For:“-Headers ersetzt (wo der herkommt, dazu später mehr). Debian hat’s praktischerweise als fertiges Paket herumliegen, so dass man das Problem mit:
apt-get install libapache2-mod-rpaf cd /etc/apache2/mods-enabled ln -s ../mods-available/rpaf.* .
erledigt hat.
Das https-Caching ist mit WordPress als Backend komplizierter; da WordPress viel mit vollständigen (auch selbst erzeugten) URLs arbeitet, wird man beim einfachen Ansatz (also einen TLS-Offloader vor den Proxy-Cache setzen) ständig auf die unsichere Verbindung umgeleitet. Die eine Seite wird also wirklich über die verschlüsselte Verbindung geladen, alle Bilder und StyleSheets aber schon wieder unverschlüsselt, und die Links auf des Seite führen auch alle zur unverschlüsselten Version.
Mein erster Ansatz war, nginx die TLS-Verarbeitung durchführen zu lassen (das ging gut damit) , und Links auf http://dentaku.wazong.de/ automatisch zu https://dentaku.wazong.de/ umschreiben zu lassen. Leider ist das nicht die einzige Domain, die auf diesem Server wohnt, und das zuerst geeignet erscheinende HttpSubModule kann leider keine regulären Ausdrücke verarbeiten und ist auch auf eine Ersetzungs pro Virtual Host beschränkt. Ich ließ also nginx wieder nginx sein (wer will schon einen kompletten Webserver vor einem anderen Webserver haben?) und kehrte zu einem zweiten Versuch mit Pound zurück.
In den folgenden Artikeln erkläre ich also ein Setup, das etwa so aussieht:
Teil eins beschäftigt sich mit WordPress und https trotz Cache:
a) Woher weiß eigentlich WordPress, wann https gefragt ist?
In WordPress gibt es eine Zentrale Funktion is_ssl() in wp-includes/functions.php, die erst nach dem Serverparameter HTTPS sucht, und wenn es den gibt, und er auf „on“ oder „1“ steht true zurückgibt. Gibt es den Parameter nicht, oder hat er einen andern Wert, so wird geprüft, ob der Serverparameter SERVER_PORT auf „443“ gesetzt ist (Standardport für https). Ist das der Fall, wird true zurückgegeben, anderenfalls false:
function is_ssl() { if ( isset($_SERVER['HTTPS']) ) { if ( 'on' == strtolower($_SERVER['HTTPS']) ) return true; if ( '1' == $_SERVER['HTTPS'] ) return true; } elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) { return true; } return false; }
Den Serverport werden wir nicht beeinflussen können, aber andere Parameter kann man mit mod_rewrite (in den meisten WordPress-Installationen schon wegen der Permalinks vorhanden) setzen. Dazu markiere ich mit Pound die https-Verbindungen durch ein zusätzliches Request-Header-Feld (was das ist erläutere ich im Teil b):
AddHeader "X-Pound-HTTPS: on"
Dessen Existenz prüfe ich dann in einer Rewrite-Regel und setze gegebenenfalls den Serverparameter:
RewriteCond %{HTTP:X-Pound-HTTPS} =on RewriteRule .* - [E=HTTPS:on]
Jetzt weiß WordPress, wenn die Verbindung durch Pound kam, dass die Seite mit https-Links erzeugt werden soll. Ruft man dieselbe Seite aber nacheinander erst einmal mit http und dann einmal mit https auf, dann bekommt man das gleiche Ergebnis — aus dem Cache, ohne dass WordPress von der Anfrage überhaupt etwas mitbekommt. Daher:
b) Welche Objekte legt Varnish eigentlich getrennt im Cache an?
Eine http-GET-Anfrage besteht aus dem eigentlichen „Befehl“ (also z.B. „GET /tag/blog HTTP/1.0“) und einer Reihe von Zusatzfeldern, den sogenannten Request-Headern. Darin stehen eine Menge eher statistische Informationen wie z.B. die Seite, von der man auf diesen Link gekommen ist (oder auch für die man dieses Bild nachlädt) — „Referer:“, mit welchem Browser man so unterwegs ist — „User-Agent:“, welche Zeichencodierungen der so verstehen kann — „Accept-Charset:“ aber auch wichtige Dinge wie von welchem Servernamen man die angegebene Seite überhaupt laden möchte — „Host:“.
Varnish legt standardmäßig einen getrennten Cacheeintrag für jede Kombination aus Requestpfad und „Host:“-Header an.
Die Antwort eines http-Servers enthält ebenso (neben den angefragten Daten natürlich) eine Sammlung von Response-Headern. Davon interessiert uns der „Vary:“-Header, mit dem der Server anzeigen kann, der Inhalt welcher (weiteren) Request-Header die Auslieferung anderer Daten zur Folge gehabt hätte. Ein typisches Beispiel ist „Vary: Accept-Encoding“, womit der Server sagt, dass er die Seite auch noch komprimiert (oder unkomprimiert oder anders komprimiert) hätte ausliefern können. Varnish würde sich in diesem Fall eine Variante der Seite für Browser merken, die gzip-Encoding verstehen und eine für andere (das ist auch in Wirklichkeit etwas komplizierter, aber für den Moment reicht’s so). Die oben vorgestellte Rewrite-Regel sorgt jetzt schon dafür, dass der abgefragte Request-Header in den „Vary:“-Response-Header aufgenommen wird, wenn die Bedingung erfüllt ist. Leider reicht uns das nicht: trifft die Bedingung nämlich nicht zu, so erfährt Varnish nicht davon, dass es noch eine andere Variante gegeben hätte. Mit dem Apache-Modul mod_headers muss daher nachgeholfen werden. Der folgende Eintrag (z.B. in der .htaccess-Datei):
Header merge Vary X-Pound-HTTPS
setzt den gewünschten „Vary:“-Header auch für die http-Anfragen. Dadurch werden in Varnish unterschiedliche Versionen der Seite für http und für https im Cache gehalten.
Im nächsten Teil geht’s um Hitraten und Cachekontrolle.
4 Antworten auf „Mein Speed-Setup, Teil1: https“
[…] wahrscheinlich etwas wirren) Serie auf dem Weg zum schnellen Webserver mit (/trotz?) WordPress: http://shxz.de/fl # Microblog […]
Hey, das sind ja ausgeklügelte Ideen. Darüber hatte ich noch gar nicht nachgedacht.
[…] Teil 1 haben wir Varnish (und Pound) vor WordPress gestellt. Das ergibt erstmal nur eine geringe […]
[…] Teil 3: Fixing und Tuning Ein Artikel aus der Reihe, in der ich mein Serversetup erkläre (Teil 1, Teil 2). Am Setup wird nach wie vor geschraubt, da gäbe es also eigentlich durchaus noch was […]