
Website-Performance Mythen
Seit Google die Ladezeiten zu einem Rankingfaktor erklärt hat, haben viele Web-Entwickler das Thema auf der Agenda. Mit der Einführung von HTTP/2 verbesserte sich die Geschwindigkeit in vielen Fällen bereits mit der Aktivierung des neuen Protokolls, ohne dass sich die Entwickler mit dem Thema intensiver auseinandersetzen mussten. Dadurch und mit der Einführung neuer Web-Browser- Technologien geistern immer noch viele Mythen zur Website-Performance durch verschiedene Blogs, die häufig die Codequalität einer Seite verschlechtern oder die sich sogar negativ auf die Ladezeit auswirken. Im Vortrag „Website-Performance Mythen – warum viele Empfehlungen mehr schaden als nutzen“, den ich am 5. Februar 2020 auf der Konferenz c´t webdev in Köln gehalten habe, zeigte ich auf, welche typischen Irrtümer es gibt und welche Methoden die Ladezeit wirklich verbessern. Im Anschluss gab ich einen Ausblick auf die neuen Features von HTTP/3.
Website Geschwindigkeit
Um ein positives Nutzererlebnis zu schaffen, ist die Geschwindigkeit einer Website wichtig. Dieses Thema wurde sehr stark forciert, nachdem Google 2010 bekanntgegeben hatte, dass die Geschwindigkeit Einfluss auf das Ranking hat. Außerdem wurde Anfang 2018 noch deutlicher auf die Position der Google-Suchergebnisse von Mobile-Websites in Abhängigkeit der Geschwindigkeit hingewiesen.


Bei der Ladezeit geht es um die Zeitspanne der Anfrage durch den Client, bis die Antwort des Webservers zum Client übertragen worden ist. Dazu gehört die DNS-Anfrage, der TCP-IP-Verbindungsaufbau, die TLS-Verschlüsselung, die Bearbeitung auf dem Server und anschließend die Übertragung zum Client. Wobei die reine Downloadzeit in Deutschland immer weiter an Bedeutung verliert, da gemäß dem Speedtest Global Index eine Durchschnittsgeschwindigkeit von 77,57 MBit/s (Mobil 36,04 MBit/s) in Deutschland erreicht wird. Der nachfolgende Screenshot zeigt die Ladezeit der Startseite der Domain ctwebdev.de, wobei die reine Übertragungszeit (Download) mit 0 Millisekunden angeben wird, da er (der Download) so schnell erfolgte.
Die Verarbeitungszeit umfasst die Interpretation der Ressourcen durch den Client sowie die Analyse der HTML-, CSS- und JavaScript-Dateien. Erst nachdem diese verarbeitet wurden, kann der Webbrowser mit der Darstellung beginnen. Häufig ist die Verarbeitungszeit größer als die Ladezeit. Der nachfolgende Screenshot zeigt die Verarbeitungszeit der Startseite der Domain ctwebdev.de in Chrome. Die reine Ladezeit aller Ressourcen betrug 15 Millisekunden. Die Verarbeitungszeit betrug hingegen 65 Millisekunden, 16 Millisekunden für die JavaScript Verarbeitung, 38 Millisekunden für die Verarbeitung des DOM und 11 Millisekunden für die Darstellung.
Grundsätzlich gilt, dass alle Berechnungen, die bei jedem Seitenaufruf durchgeführt werden müssen oder Ressourcen, die verarbeitet werden müssen, ohne dass sie genutzt werden, entfernt oder minimalisiert werden müssen.
PHP-Version
Bei fast allen Tipps wird aufgeführt, dass auf die aktuellste PHP-Version gewechselt werden soll, um die Geschwindigkeit zu verbessern. Grundsätzlich spricht nichts gegen einen Wechsel, da es allein aufgrund der Sicherheit und Zukunftsfähigkeit der Website sinnvoll ist. Aber auf die Geschwindigkeit sind die Effekte oft sehr gering bzw. gar nicht vorhanden.
Zwar wird in den größeren Releases von PHP oft aufgeführt, dass die Performance deutlich gesteigert worden ist. Dabei werden Formulierungen wie „eine Verdopplung bei der Performance gegenüber dem Vorgänger-Release“ verwendet. Allerdings bezieht sich diese Aussage immer auf einzelne Funktionen. Und nur wenn diese auch in der Web-Anwendung wirklich genutzt werden, kann der Vorteil genutzt werden. Daher betrifft dieser angepriesene Vorteil häufig nur einen kleinen Teil der Anwendungen. Auf den anderen Seiten nutzen Websites, bei denen die Geschwindigkeit optimiert worden ist, häufig Caching Mechanismen. Daher wird ein Großteil der Ressourcen wie statisch generierter Code, Abbildung, CSS- und JavaScript-Dateien sowieso ohne das Zutun von PHP ausgeliefert. Daher ist der Geschwindigkeitsvorteil durch eine neue PHP-Version häufig nicht messbar.
In künstlichen Benchmarks mit WordPress in der Version 5.0 mit deaktiviertem Caching und ohne Optimierungen wurde eine durchschnittliche Steigerung von ca. 7,08% der Anzahl an Anfragen pro Sekunde pro PHP 7.x Version erreicht. Und dies bezieht sich nur auf den Zeitraum der Bearbeitung auf dem Server, was wiederum bei einer normalen Website lediglich einen kleinen Bruchteil ausmacht.


Webbrowser Caching
Das Caching, also das Zwischenspeichern von generierten oder geladenen Inhalten, ist eine sehr effektive Maßnahme, um die Geschwindigkeit zu erhöhen. Auf der Serverseite ist es immer von Vorteil, wenn Ressourcen effektiv zwischengespeichert werden. Gerade die Generierung von statischen Dokumenten aus dynamischen Ressourcen kann die Geschwindigkeit einer Website deutlich erhöhen. Dies wird von vielen Content-Management-Systemen unterstützt. Nach einer Änderung werden automatisch die statischen Dokumente neu generiert.
Auf der Client-Seite besteht ebenso ein Cache. Dieser wird über einen Header-Eintrag gesteuert. Häufig ist die Empfehlung Werte zu setzen, damit die Inhalte möglichst lange zwischengespeichert werden. Viele setzen dies auch um, jedoch für alle Ressourcen. Dabei gibt es immer wieder Ressourcen, die dynamische Inhalte beinhalten, die sich mit jedem Aufruf ändern. Dies betrifft vor allem Web-Anwendungen wie Dashboards. Wird hier das Caching deaktiviert, wird der Client entlastet, da er nicht bei jedem Aufruf einen neuen Eintrag im Cache auf der Festplatte erstellen muss. Außerdem sollten alle sicherheitskritischen Seiten, die zum Beispiel Zahlungsdaten beinhalten, nie im Cache abgelegt werden.
Gleichzeitig wird zur Validierung des gespeicherten Objektes eine Anfrage an den Server mit einer eindeutigen Kennung (etag) übermittelt. Stimmt diese Kennung überein, muss diese Ressource nicht neu geladen werden und der Server quittiert die Antwort mit dem Status-Code 304. Dies sorgt für eine Verzögerung, da die Kommunikation Zeit benötigt. Hier schafft der Eintrag „Cache-Control: immutable“ Abhilfe. Damit wird der Webbrowser angewiesen, Ressourcen aus dem Cache zu verwenden, ohne eine Validierung durchzuführen. Dies macht einen großen Unterschied in Bezug auf die Performance aus, wie das folgende Beispiel mit Facebook zeigt. Leider wird diese Funktion im Moment von Google Chrome noch nicht unterstützt.
Vektorgrafiken
Um eine größere Anzahl kleiner Grafiken wie zum Beispiel Icons schnell laden zu können, war eine der ersten Empfehlungen, die Dateien in einer großen Abbildung zusammenzufassen. Anschließend werden mit CSS-Anweisungen einzelne Ausschnitte dargestellt. Dadurch wird die Anzahl der Anfragen reduziert und somit die Geschwindigkeit der Website verbessert. Mittlerweile werden häufiger Vektorgrafiken eingesetzt. Vektorgrafiken basieren auf einer mathematischen Beschreibung einer Abbildung durch die Angaben der Koordinaten einzelner Punkte und ihren Verbindungen untereinander. Der Vorteil liegt in der geringeren Dateigröße und dass sie in der Darstellungsgröße ohne Qualitätsverluste beliebig skaliert werden können.
Dafür werden häufig Icon Schriftarten verwendet. Sie können einfach eingebunden werden und funktionieren auch in flexiblen Layouts, die zudem ohne Probleme beliebig skaliert werden können. Eine der bekanntesten Schriftarten ist die Font Awesome, die in der kostenlosen Version über 1500 Icons umfasst. Allerdings werden die Icon Fonts auch bei kleineren Projekten verwendet, die nur einige wenige Icons davon verwenden. Dadurch wird trotzdem ein kompletter Satz von Icons geladen, wodurch unnötig Bandbreite genutzt wird. Zusätzlich wird eine große CSS-Datei mit vielen Anweisungen geladen, da jedes Icon eine eigene CSS-Klasse benötigt. Dies trägt wiederum zu einer Erhöhung der Verarbeitungszeit im Webbrowser bei.
Eine elegantere Lösung ist die Verwendung von Diensten, die einen individuellen Schriftensatz generieren. Hierbei kann ausgewählt werden, welche Icons verwendet werden sollen. Im Anschluss daran wird eine individuelle Icon Font generiert und zum Download angeboten. Dies reduziert sowohl die Lade- als auch die Verarbeitungszeit.
Die nachfolgende Abbildung zeigt den Vergleich von Font Awesome in voller Größe (CSS + TTF = 423 KB), komprimiert (CSS minify + WOFF2 = 220 KB) und eine generierte Version mit zehn Icons.


Komprimierung
Eine häufige Empfehlung ist es, die Ladezeit durch die Aktivierung der Komprimierung zu reduzieren. Dabei werden auf der Serverseite die Dateien vor der Übertragung komprimiert und erst danach übertragen. Der Client entpackt die Daten wieder und verarbeitet diese. Es gilt dabei zu überprüfen, ob der Webbrowser die gewählte Komprimierungsart unterstützt. Grundsätzlich handelt es sich hierbei um eine einfache und effektive Methode, um die erforderliche Bandbreite zu reduzieren.
Wichtig ist allerdings zu beachten, dass sich verschiedene Dateitypen unterschiedlich stark komprimieren lassen. So können beispielsweise Textdateien sehr gut komprimiert werden, wohingegen Binärdateien nur minimal komprimiert werden. Als Konsequenz daraus sollten demnach nur Textdateien wie zum Beispiel HTML-, CSS- und JavaScript-Dateien komprimiert werden. Werden hingegen JPEG-Dateien komprimiert, ist die Reduzierung der Dateigröße nur minimal und daher kann davon ausgegangen werden, dass Ressourcen verschwendet werden und damit die Geschwindigkeit der Website negativ beeinflusst wird.
Wird eine Website oder Web-Anwendung von vielen Nutzern aufgerufen, erfolgt die Komprimierung grundsätzlich sehr schnell, benötigt jedoch eine gewisse Rechenleistung und entsprechenden Arbeitsspeicher. Gerade der in Bezug auf die Dateigröße effiziente „brotli“ Komprimierungsalgorithmus benötigt mehr Rechenleistung. Steht auf dem Webserver genügend Bandbreite zur Verfügung und handelt es sich bei der Rechenleistung (Auslastung des Prozessors) oder dem Arbeitsspeicher um die limitierenden Faktoren, kann die Komprimierung dynamisch deaktiviert werden. Damit wird es möglich, eine höhere Anzahl an Requests zu verarbeiten, womit zeitgleich die Anzahl der abgebrochenen Requests reduziert wird. Bei diesen Voraussetzungen kann die beschriebene Vorgehensweise sogar eine Reaktion auf einen DDOS-Angriff sein.
Die nachfolgende Abbildung zeigt die durchschnittliche Kompressionsrate der Algorithmen „gzip“ und „brotli“ im Vergleich. Zusätzlich wird die benötigte Zeit zum Dekomprimieren und Komprimieren verglichen.


Ein gravierender Nachteil der Komprimierung im Apache Webserver mit dem Modul mod_deflate liegt darin, dass komprimierte Inhalte nicht zwischengespeichert werden. Dies führt dazu, dass häufig genutzte Ressourcen nicht im schnellen Arbeitsspeicher Cache abgelegt werden und bei jedem Aufruf erneut Rechenleistung für die Komprimierung benötigt wird. Daher lautet die Empfehlung, die Ressourcen im Build-Prozess vorab zu komprimieren und zu speichern. Damit wird die komprimierte Ressource direkt ausgeliefert, ohne dass jedes Mal eine erneute Komprimierung durchgeführt werden muss.
HTTP Requests
Ein sehr effektives Mittel, die Geschwindigkeit der Website zu erhöhen, ist die Reduzierung der Anzahl der http-Anfragen. Jede Anfrage nutzt eine eigene Netzwerkverbindung und benötigt auch einen eigenen HTTP-Header – es kommt somit zu einer gewissen zeitlichen Verzögerung des Seitenaufrufs. Sollten zudem Ressourcen an der falschen Stelle eingebunden sein, (dann) werden weitere Anfragen blockiert, bis die vorherige Anfrage geladen und verarbeitet worden ist. Daher ist eine gängige Empfehlung, alle CSS- oder JavaScript-Ressourcen zu jeweils einer Anfrage zusammen zu fassen.
Dies sorgt zwar für deutlich weniger http-Requests, jedoch entstehen dadurch riesige Dateien, wofür wiederum der Webbrowser viel Zeit zur Bearbeitung benötigt. Bis dahin wird die Website für den Benutzer nicht dargestellt und dieser empfindet sie somit als langsam. Ein häufig beschriebener Lösungsansatz besteht darin, die wichtigen CSS-Anweisungen direkt in den Kopfbereich des HTML-Dokuments zu schreiben. Dadurch wird der für den Nutzer direkt sichtbare Teil der Website bereits dargestellt und währenddessen werden noch die anderen Dateien geladen. Das sorgt allerdings meist für eine schlechtere Codequalität und die Ressourcen können nicht mehr zwischengespeichert werden. Ab dem zweiten Aufruf hat dies in Summe eine negative Auswirkung auf die Geschwindigkeit der Website. Daher sollten besser mehrere Request in Kauf genommen werden. Hier sollten Methoden eingesetzt werden, um Dateien intelligent nachzuladen.
Eine sinnvolle Variante zur Vermeidung der oben beschriebenen Nachteile liegt darin, JavaScript im Header mit den Attributen async bzw. defer einzubinden.
Die Funktion async sorgt dafür, dass die JavaScript-Datei parallel geladen und nach dem Ladevorgang die Bearbeitung der HTML-Datei unterbrochen sowie die JavaScript-Datei interpretiert und ausgeführt wird. Anschließend wird die Bearbeitung der HTML-Seite vorgeführt.
<script async src="script.js">
Das Attribut defer sorgt dafür, dass der Webbrowser die JavaScript-Datei nur herunterlädt. Mit der Interpretation und Ausführung wird gewartet, bis die HTML-Seite fertig bearbeitet wurde.
<script defer src="script.js">
Eine weitere sehr effektive Methode, die Geschwindigkeit einer Website mit CSS– und JavaScript-Dateien zu erhöhen, ist das Laden bei Bedarf. Denn häufig werden bestimmte Funktionen nur auf einzelnen Unterseiten eingesetzt oder die Funktionalitäten befinden sich im unsichtbaren Bereich der Website. Es sollten daher beim Seitenaufruf nur die notwendigsten Teile geladen werden, um dann bei Bedarf für einzelne Funktionen separate Dateien nachzuladen. Dazu wird überprüft, ob ein Element auf der Seite existiert und dann werden die entsprechenden Dateien nachgeladen.
Eine weitere Möglichkeit Ressourcen effizienter parallel zu laden, besteht durch den Einsatz von HTTP/2. Mit dem Push-Verfahren kann dem Webbrowser bei der ersten Verbindung mittgeteilt werden, welche zusätzlichen Ressourcen geladen werden sollen. Daher beginnt der Webbrowser den Download von mehreren Dateien, bevor das eigentliche HTML-Dokument ausgewertet wurde. Deshalb auch der Name Push. Hier schiebt der Webserver bereits die Dateien zum Webbrowser, bevor dieser überhaupt weiß, an welcher Stelle diese später zum Einsatz kommen. Dies erhöht die Ladezeit einer Website enorm.
HTTP/3
Mit HTTP/2 wurden bereits einige Probleme gelöst, die bis dahin einen Flaschenhals in der Kommunikation dargestellt haben. So wurde eine Kompression der Header eingefügt, indem ein Wörterbuch mit den häufigsten Einträgen verwendet wird. Anschließend müssen dann nur noch Zahlen für die Einträge verwendet werden. Zusätzlich werden mit HTTP/2 über eine TCP/IP Verbindung mehrere Dateien gleichzeitig übertragen, ohne jedes Mal eine neue TCP/IP Verbindung zu öffnen, was zusätzliche Zeit benötigt. Der Nachteil ist hier, dass die Dateien in der Reihenfolge der Anforderung übertragen werden müssen und wenn ein Datenpaket verloren geht, müssen alle Dateien neu übertragen werden. Hier kann bereits ein 2 prozentiger Datenverlust bei der Übertragung die Vorteile von HTTP/2 komplett eliminieren. Gleichzeitig sorgt eine Änderung der IP-Adresse, wie sie zum Beispiel beim Smartphone durch den Wechsel vom WLAN zum Mobilfunknetz stattfindet, dafür, dass die Verbindung komplett abgebrochen wird.


An dieser Stelle setzt HTTP/3 an. Es verwendet anstatt TCP das zustandlose Netzwerkprotokoll UDP zur Übertragung und setzt (drauf) ein eigenes Protokoll auf. Daher ist HTTP/3 mehr als nur ein neuer HTTP-Standard. Die Basis für die Entwicklung wurde von Google mit QUIC entwickelt. Dies ist auch der Grund, warum dieser Name auch für das Protokoll verwendet wird.
Bei UDP liegt der Fokus auf der Geschwindigkeit, so wird im Gegensatz zu TCP nicht jede Verbindung ausgehandelt und von der Gegenstelle bestätigt, sondern die Übertragung direkt gestartet. Dadurch kann eine verschlüsselte Verbindung mit weniger Kommunikationoverhead gestartet werden, wodurch weniger Datenpakete erforderlich sind.
Jede Verbindung zwischen einem Client und einem Server bekommt eine eindeutige ID zugewiesen. Damit ist es möglich, die IP-Adresse zu wechseln, ohne dass die Übertragung unterbrochen wird. Gleichzeitig gibt es keine starre Reihenfolge der Dateien, wodurch es keine Warteschlangen gibt und die Abfragen parallel abgearbeitet werden können.
Als zusätzliche Features setzt HTTP/3 auf eine konsequente Verschlüsselung. Das heißt, eine unverschlüsselte Verbindung ist nicht mehr möglich. Hierzu wird TLS in der Version 1.3 eingesetzt. Allerdings wird dies fest im QUIC Protokoll integriert und nicht wie bisher als extra Schicht umgesetzt.
Fazit
Bei der Optimierung der Geschwindigkeit einer Website bzw. Web-Anwendung gilt immer die Faustregel: Alle Berechnungen, die bei jedem Seitenaufruf durchgeführt werden müssen oder Ressourcen, die verarbeitet werden müssen, ohne dass sie genutzt werden, müssen entfernt oder minimiert werden.
Ein großer Fokus bei der Website-Performance liegt auf der Verkleinerung der zu übertragenden Dateigrößen und in der Reduzierung der Anzahl der Anfragen. Bereits mit der Einführung von HTTP/2 standen hier sehr wirkungsvolle Mechanismen zur Verfügung, die mit dem Nachfolgestandard HTTP/3 ergänzt werden und damit die Übertragungsgeschwindigkeit noch einmal deutlich steigern werden. Dies führt voraussichtlich dazu, dass sich der Fokus bei der Optimierung der Geschwindigkeit einer Website bzw. Web-Anwendung vermutlich mehr auf die Interpretationsdauer und die Rendering-Zeiten im Client verlagern wird.
Den Vortrag „Website-Performance Mythen – warum viele Empfehlungen mehr schaden als nutzen“ habe ich am 5. Februar 2020 im Rahmen der Konferenz c’tWebDev in Köln gehalten (PDF Download).
Danke Tobias für die hilfreichen Infos!
Werde direkt mal starten, meine Seite zu überarbeiten.
Grüße, Peter