Aureola optimieren (2/5): JPG & PNG automatisch als WEBP ausliefern

Nachdem ich im letzten Artikel bereits die gzip-Kompression unter Nginx aktiviert habe und damit ein paar Kilobyte einsparen konnte, widme ich mich in diesem Artikel der größten Baustelle: den Bildern. Ich zeige Euch, wie Ihr das Bildformat webp nutzen könnt, um Eure Bilddaten drastisch zu komprimieren.

Was bisher geschah

Little Astronaut - Now It
Little Astronaut - Now It's Personal - gzip aktiviert
Was bringt eine Promotion bei Instagram? - gzip aktiviert
Was bringt eine Promotion bei Instagram? - gzip aktiviert

Schauen wir uns zunächst einmal an, wo wir zuletzt stehen geblieben waren. Die Downloadgröße beträgt nun 866 kb und 1,6 MB. Die Antwortzeit stieg leicht auf 359 ms und 459 ms. Der größte Teil der Downloadgröße geht auf die in den Artikeln eingebundenen Bilder zurück. Derzeit werden sie als jpg und png ausgeliefert. Aber es gibt noch ein weiteres Format, welches deutlich bessere Komprimierungsoptionen bietet: webp.

Triff WEBP

Webp ist ein Bildformat, welches von Google entwickelt wurde. Es bietet die MöglichkeitBilder verlustfrei, aber auch verlustbehaftet zu komprimieren. Dabei sind die erzeugten webp-Bilder laut Eigenwerbung bis zu 34% kleiner (26% bei verlustfreier Kompression) als vergleichbare Formate. Der Browser-Support ist mittlerweile relativ gut, ein Fallback sollte aber dennoch in Erwägung gezogen werden.

Zur Umwandlung stellt Google einige Tools zur Verfügung. Wer sich mit der Command Line nicht wohlfühlt, kann aber auch auf Online-Tools oder andere Software zurückgreifen. Ich habe mich für cwebp entschieden, da ich das Tool in meinen Deployment-Prozess integrieren möchte.

Die Installation kann unter Ubuntu einfach aus den Paketen geschehen:

$ apt update
$ apt install webp -y

Anschließend steht das Tool cwebp über die Kommandozeile zur Verfügung. Ohne nennenswerte Settings definieren zu wollen, kann man die Konvertierung dann so anstoßen:

$ cwebp path/to/source.jpg -o path/to/target.webp

Standardmäßig wird eine verlustbehaftete Kompression mit einer Qualitätseinstellung von 75% verwendet. Stellt man hier einmal beispielhaft Original und konvertiertes Bild nebeneinander, merkt man, dass dabei einige Artefakte entstehen und die Qualität abnimmt. Was hier tolerierbar ist, hängt sicherlich vom Einzelfall ab. In meinem doch recht Textlastigen Blog dienen Bilder vor allem der Illustration. Der Qualitätsverlust ist (Stand jetzt) für mich also auf jeden Fall zu verschmerzen. Die Art und Qualität der Komprimierung ist natürlich komplett einstellbar.

JPG vs WEBP
JPG vs WEBP

Mit Nginx automatisch Webp ausliefern

Wie im vorherigen Artikel bereits erwähnt, betriebe ich einen Nginx-Server. Diesen können wir nutzen, um automatisch webp-Dateien auszuliefern, sofern der Browser dies unterstützt und es eine webp-Alternative für eine Datei gibt. Natürlich könnt Ihr auch einfach webp-Bilder anstatt von jpg oder png in Eure Seite einbinden. Aber das schließt dann natürlich Browser dies das nicht unterstützen explizit aus.

Bei Nginx überprüfen wir zunächst einmal, ob der Browser webp überhaupt unterstützt. Hierfür erstellen wir eine Config-Datei /etc/nginx/conf.d/webp.conf mit folgendem Inhalt:

map $http_accept $webp_suffix {
    default   "";
    "~*webp"  ".webp";
}

Wir testen hier, ob der Browser einen Accept-Header für webp gesendet hat und setzen anhand dessen eine Variable mit dem Namen $webp_suffix auf den Wert .webp. Ist das nicht der Fall, wird als default ein leerer String definiert. Diese Variable brauchen wir, um Requests von Bilddateien automatisch umleiten zu können.

Für diese Umleitung braucht es nur ein paar Zeilen in Eurer Seitenkonfiguration. Der Einfachheit halber machen wir das wieder in Form eines Snippets /etc/nginx/snippets/webp.conf:

location ~* ^.+\.(jpe?g|png)$ {
   add_header Vary Accept;
   try_files $uri$webp_suffix $uri =404;
}

Übersetzt bedeutet die Konfiguration: Wenn im Zielpfad die Endung jpg, jpeg oder png vorkommt, versuche Dateien auszuliefern (try_files). Und zwar entweder die Original-Url mit dem $webp_suffix oder aber einfach die Original-Datei. Sollte keine der Dateien gefunden werden, gib einen 404-Fehler zurück. Für eine Datei path/to/source.jpg wird also zunächst versucht path/to/source.jpg.webp auszuliefern. Ist das $webp_suffix nur ein leerer String, so sind $uri$webp_suffix und $uri gleich. Es wird also einfach die Original-Datei ausgeliefert.

Dieses Snippet binden wir einfach in der Seitenkonfiguration unter /etc/nginx/sites-available ein:

server {
  ...
  include /etc/nginx/snippets/webp.conf;
  ...
}

Anschließend überprüfen wir die Konfiguration und laden den Nginx-Service neu:

$ nginx -t
$ service nginx reload

Nun werden bereits automatisch webp-Dateien ausgeliefert, sofern diese vorhanden sind. Bitte beachtet hier die Namensregel: Für eine Datei source.jpg, muss es eine Datei source.jpg.webp geben.

Automatisierung

Nun könnte ich natürlich jede Datei einzeln konvertieren. Aber dafür bin ich um ehrlich zu sein viel zu faul. Stattdessen geht das viel einfacher über ein kleines Bash-Skript:

images=$(find your/images/folder -type f \( -iname "*.jpg" -o -iname "*.png" \))
for source in $images; do
  target="$source.webp"
  if [ ! -f $target ]; then
    echo "Processing $source..."
    cwebp -quiet $source -o $target
  fi
done

Das Skript sucht hier im Ordner your/images/folder nach Dateien mit den Endungen jpg oder png. Dann wird für jede einzelne Datei überprüft, ob es bereits eine Entsprechung mit webp-Endung gibt. Wenn nicht, wird cwebp verwendet, um eine komprimierte Variante zu erstellen. Dies nutze ich beispielsweise innerhalb meines Deployment-Skripts. Ihr könnt das zum Beispiel in einem Crontab verwenden oder auch bei Bedarf von Hand anstoßen.

Ergebnis mit webp-Bildern

Little Astronaut - Now It
Little Astronaut - Now It's Personal - gzip aktiviert & webp-Bilder
Was bringt eine Promotion bei Instagram? - gzip aktiviert & webp-Bilder
Was bringt eine Promotion bei Instagram? - gzip aktiviert & webp-Bilder

Die Einsparungen hier sind gewaltig. Von 866 kb und 1,6 MB reduziert sich die Downloadgröße auf 495 kb und 375 kb. Die Antwortzeiten reduzieren sich von 359 ms und 459 ms auf 279 ms und 265 ms. Und besonders schön daran ist, dass dafür rein gar nichts am CMS oder den bestehenden Bildern geändert werden musste. Stattdessen geschieht die Umwandlung und die Auslieferung via Nginx heimlich, still und leise im Hintergrund. Das ist ein schöner Erfolg, aber es geht noch besser. Im nächsten Artikel beschäftige ich mich mit der Optimierung der Icon-Font.

Dieser Artikel ist Teil einer Serie. Hier geht's weiter: