Website-Performance tunen mit YSlow

Ich wollte eigentlich schon vor längerer Zeit mal was über YSlow schreiben, weil es wieder mal ein kleines und feines Addon für den Firefox und Firebug ist. yahoo! widmet sich ja in letzter Zeit sehr der Untersuchung von Website-Performance und auf diesem Weg ist dann auch YSlow entstanden, mit welchem man nicht nur die Performance von Websites nach 13 Kriterien testen kann, sondern auch gleich Hinweise zur Verbesserung bekommt.

Eben habe ich dann diesen Artikel über die einzelnen Kriterien und die Optimierung dafür in Ruby on Rails gefunden und dachte mir, dass es sich lohnt, das Thema aufzugreifen und hier auch mal die Punkte einzeln durchzugehen und zu sagen, was ich bei wevent getunt habe.

Make Fewer HTTP Requests, Minify JavaScript

Je weniger HTTP-Requests (also Laden einzelner Dateien vom Server) gemacht werden müssen, desto schneller kann die gesamte Seite ausgeliefert werden. Hier ist es also optimal, wenn sich alle Styles in einer CSS-Datei befinden und der komplette JavaScript-Code auch in einer einzelnen Datei steht.

Rails bringt ja von Haus aus schon vier verschiedene JavaScript-Dateien mit sich, allerdings gibt es hier das sehr gute Plugin asset_packager, welches nicht nur alle Script-Dateien zu einer zusammenführt, sondern das Ergebnis anschließend auch noch komprimiert. Dadurch reduziert man nicht nur die HTTP-Requests, sondern auch die Dateigröße - leider fehlen in Prototype ein paar Semikolons, so dass die Library nicht komprimiert werden kann (soll aber scheinbar noch behoben werden).

Was Bilder angeht, so sollte man für den Fall, dass man bspw. eine grafische Navigation auf der Website hat, möglichst keine Einzelbilder für alle Zustände verwenden, sondern alles in einem Bild zusammenfügen und dieses dann entsprechend als Hintergrund positionieren.

Put CSS at the Top, Move Scripts to the Bottom

Dürfte klar sein… CSS läd nach yahoo!-Erkenntnissen am schnellsten, wenn es im HEAD per link-Element eingebunden wird (kann man mit dem asset_packager Plugin per stylesheet_link_merged :base machen) und JavaScript-Dateien sollten möglichst am Ende des BODY referenziert werden (javascript_include_merged :base).

Das mit dem JavaScript so weit wie möglich unten hat mich zunächst verwundert, jedoch klingen die Erklärungen dazu einleuchtend: So lange ein Script vom Browser geladen wird, werden keine Elemente unterhalb des script mehr gerendert - also möglichst weit nach unten damit.

Remove Duplicate Scripts, Make JavaScript and CSS External

Die beiden Tipps sind eigentlich No-Brainer: Ein und dieselbe JavaScript-Datei nur einmal einbinden - dürfte klar sein, oder? Genauso, dass möglichst wenig CSS und Javascript inline, also direkt im Quelltext verwendet werden sollten - nicht nur unkool weil nicht unobtrusive, sondern weil es inline auch nicht gecacht werden kann.

Use a Content Delivery Network

Oha! Das spielt denke ich für den Großteil von uns keine Rolle, macht für yahoo! allerdings natürlich Sinn… ich kenn mich damit nicht aus, daher lasse ich mal jegliche Äußerungen dazu. Im anderen Artikel wird dazu Amazons S3 Service als Startpunkt dafür vorgeschlagen - das scheint mir allerdings eher eine Lösung die in Richtung Asset-Host geht zu sein.

Gzip Components

Hier ist mir aufgefallen, dass wir bisher zu viel verschenkt haben, dadurch dass wir einfach nicht danach geguckt haben: Die Inhalte vor dem Senden an den Browser komprimieren. Dadurch können so ca. 75% der Dateigröße eingedampft werden, es wird von mittlerweile 90% der Browser unterstützt und selbst ich als Server-Newbie hab das in 10 Minuten eingerichtet bekommen (um das mal in überzeugenden Zahlen auszudrücken).

So hab ichs bei unserem Apache 2.2 gemacht: Zuerst muss - sofern nicht schon erledigt - das deflate-Modul aktiviert werden. Dazu das Modul und seine Config versymlinken:

$ /etc/apache2/mods-enabled# ln -s /etc/apache2/mods-available/deflate.load
$ /etc/apache2/mods-enabled# ln -s /etc/apache2/mods-available/deflate.conf

Danach noch kurz die Konfiguration anpassen:

AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/x-httpd-php

Und den Apache reloaden: $ /etc/init.d/apache2 reload

Anschließend kann man hier testen, ob die Inhalte jetzt komprimiert gesendet werden und wie viel es gebracht hat.

Avoid CSS Expressions

CSS Expressions sind mal wieder so eine Microsoft-Erfindung um JavaScript in CSS zu benutzen… klingt nicht nur übel, sondern isses auch - benutzt das tatsächlich wer? Einfach Finger weg…

Add an Expires Header

Mit dem Expires Header legt man fest, wie lange der Client die Serverantwort cachen kann - quasi ein Verfallsdatum für Requests. Bei der ersten Anfrage einer URL wird ein “gültig bis”-Zeitstempel mitgeliefert und bei erneuter Anfrage der gleichen URL geht kein neuer Request an den Server, wenn das Verfallsdatum noch nicht abgelaufen ist.

Das lässt sich in der Webserver-Konfiguration vornehmen (bspw. mit mod_expires beim Apache) und sollte zumindest für die Inhalte angewandt werden, welche sich eigentlich nicht ändern (Bilder, Flash-Animationen, …). Etwas tricky wird es bei Stylesheets und JavaScripts: Werden diese auch mit einem weit in der Zukunft liegendem Expires-Datum versehen, muss bei jeder Änderung auch der Dateiname geändert werden (bspw. durch Hinzufügen einer Versionsnummer), damit die auf Clientseite gecachte Version verfällt. Auch hier wieder Thumbs-up für den asset_packager, welcher automatisch neue Versionen erkennt und diese unter Hinzufügen der SVN-Revisionsnummer speichert :)

Configure ETags

Geht in eine ähnliche Richtung wie die Expires Header: Die Entity (Bilder, Stylesheets, …) Tags werden benutzt, um eine eindeutige Repräsentation der Dateiversion an den Client zu senden. Bei erneuter Anfrage des Clients kann der Server mit Status 304 (Not Modified) antworten, wenn das vom Client gesendete ETag mit dem des Servers übereinstimmt.

Es hat ein wenig gedauert, bis ich kapiert hatte, warum es negativ ausgelegt wird, wenn man ETags verwendet - die Gründe dafür lassen sich aber auf der Diskussionsseite dazu nachlesen: Es geht darum, ETags zu deaktivieren, falls man mehrere Server verwendet, weil sie standardmäßig nicht optimal berechnet werden.. wenn ein File auf verschiedenen Servern liegt, hat es verschiedene ETags… yahoo! weisst darauf hin, dass das kontraproduktiv ist und man ETags daher nicht nutzen soll. Sofern man nur einen Server verwendet, kann die Regel ignoriert werden.

Reduce DNS Lookups

Jede im Dokument verwendete Domain muss auf die IP-Adresse aufgelöst werden, bevor der Browser anfangen kann, auf dem jeweiligen Server Dokumente anzufragen. Laut yahoo! braucht jede dieser Adressauflösungen beim ersten Mal ca. 20-120 Millisekunden und wird anschließend gecacht.

Nun ist es aber so, dass der Browser je nach Einstellungen nur eine bestimmte Anzahl von gleichzeitigen Request an die gleiche Adresse zulässt (Standard ist meistens 2). Daher heißt es in den Tipps dazu:

Avoiding DNS lookups cuts response times, but reducing parallel downloads may increase response times. My guideline is to split these components across at least two but no more than four hostnames. This results in a good compromise between reducing DNS lookups and allowing a high degree of parallel downloads.

Avoid Redirects

Sollte auch klar sein - je mehr Weiterleitungen im Hintergrund laufen, desto länger dauert es, bis die Anfrage beantwortet werden kann…

Abschließend ein kleiner Hinweis am Rand

Auf der diesjährigen @media hielt Nate Koechley einen Vortrag zur Optimierung von Website-Performance, welcher auch als Podcast runtergeladen werden kann (lohnt sich!). In naher Zukunft wird es auch ein Buch von O’Reilley zu dem Thema geben - da der Vortrag so super war, ist das schon mal direkt auf meine Wunschliste gewandert ;)

iOS app for GitHub

iOctocat

ist GitHub für die Hosentasche - deine Projekte und das was dort passiert immer dabei mit deinem iPhone und iPod Touch.
Die App ist