Tackling this above-the-fold CSS issue

Performance Optimization is somewhat like housekeeping. If you don’t take care of it regularly, the shit piles up. My history of our web performance goes back almost five years now and what started as a blazin’ 94 once was now a 76 while mobile, which was never really good, dropped from the mid-seventies to the high-fifties. Even though I optimize all images and write as less code as possible.

I came across these horrifying numbers because one of our main competitors relaunched recently and while pointing at him and laughing at his numbers (52 Desktop, 51 Mobile) I had to admit that ours aren’t really great either.

Running the Erzgebirge-Palace through PageSpeed Insights spoiled two obvious issues: The long-avoided above-the-fold render-blocking stuff and the trust seal. But that will be a different story, we will focus on the render-blocking css for now.

The core layout for Erzgebirge-Palace is ten years old now and also it has clean markup which made reponsive retrofitting quite easy there is no build process or anything near to it. So the CSS is a little bit messy. Identifying the above-the-fold styles manually was not an option, but hey, Smashing Magazine tackled this issue already two years ago. I came across this article after I unsucessfully tried to run a simple gulp task with the critical plugin by Addy Osmani.

While gulp just spit errors into my console the grunt task by Ben Zörb worked right away and returned a pretty good result. I had to add some more code as there happen things on the page the plugin cannot be aware of as different headers during the holiday season and different headers for different languages. But in the end it was less work than I expected and the result gives me now 92 on Desktop and 91 on Mobile (also resolved the trust seal, though).

All numbers given are from Google PageSpeed Insights. While this tool is ok to do a quick check on your site’s performace you should rely on other tools when actually optimizing your website, my tool of choice here is Webpagetest.

Ein CDN für den modified-Shop – xtcModified on steroids

Steter Tropfen höhlt den Stein. Augrund immer wiederkehrender Nachfragen folgt hier Teil fünf meiner kleinen Serie zur Geschwindigkeitsoptimierung von xtcModified. Es geht um die Anbindung eines CDN für statische Inhalte und daher direkt hier die Warnung: Das ist nichts für Anfänger!

Seit dem letzten Artikel sind fast drei Jahre vergangen, die Software hat sich dem xtc im Namen entledigt und der alte Testserver existiert nicht mehr.
Ich habe daher eine neue Installation aufsetzen müssen. Dieses Mal bei Uberspace. Wenn man auf der Suche nach Shared Hosting mit möglichst großer Konfigurationsfreiheit ist, dann ist das das Beste, was man für Geld bekommen kann. Ich hab natürlich die Standardinstallation erstmal grundlegend optimiert, um so Dinge wie Javascript im Footer aber einen Bogen gemacht, weil es den Aufwand für den Testbetrieb nicht rechtfertigt.

Damit auch ein paar Produkte im Shop sind wurde dieser Dump eingespielt. Anders als vorgeschlagen wurde aber nicht das Image-Processing gestartet. Wer das Image-Processing für eine gute Idee hält braucht gar nicht erst weiterlesen. Die Bilder wurden mittels Photoshop auf die passende Größe gebracht und dann mit einem mehrstufigen Prozess aus ImageOptim und JPEGmini optimiert (ähnliche Software gibt es sicher auch für Windows). Nur so hat man wirklich kleine Dateien. Und die sind aus zwei Gründen sinnvoll: Zum einen sind weniger Bytes schneller durch die Leitung geschickt, zum anderen rechnen CDNs meist nach Datenvolumen ab und hier ist jedes Byte dann bares Geld.
Der Demodump ist leider etwas kaputt, was Kategorie 1 angeht, das soll uns aber für ein Testsystem egal sein.

CDN – was ist das überhaupt?

Ein CDN, oder in Langform Content Delivery Network, ist ein aus mehreren Rechnern bestehendes Netz, das Inhalte optimiert ausliefert – entweder möglichst schnell (Performanceoptimierung) oder mit möglichst wenig Bandbreite (Kostenoptimierung) oder beides zusammen. Im konkreten Fall soll es eher um die Performanceoptimierung gehen, denn aus finanzieller Sicht wird das ganze für den Shopbetreiber auf den ersten Blick teurer als wenn er die Inhalte von seinem eigenen Server ausliefern lässt.
Das CDN macht die Auslieferung aus zwei Gründen schneller:

  • Zum einen sind die Server darauf spezialisiert, schnell statische Inhalt auszuliefern.
  • Zum anderen befinden sich die Server auf der ganzen Welt verteilt, ein Besucher aus Japan wird daher von der „Edge Location“ beliefert, die ihm physikalisch am nähsten ist. Weder die Anfrage noch die Antwort müssen um den halben Erdball, wie das bei einem in Deutschland stehenden Server der Fall wäre.

Und brauch ich das?

Wie bei so vielem ist auch hier die richtige Anwort: Kommt drauf an.
Wer einen Server hat, der in Spitzenzeiten in die Knie geht, weil der Apache nicht mehr hinterherkommt mit dem Ausliefern von Shopseiten und statischen Inhalten kann hier schnell für Abhilfe sorgen, ohne direkt den Server wechseln zu müssen.
Wer viele Kunden aus dem Ausland hat, diesen aber trotzdem Inhalte schnell ausliefern will, ist mit einem CDN ebenfalls gut bedient.
Außerdem kann man, wenn man es geschickt angeht, Browserlimitierungen umgehen was den gleichzeitigen Download von Dateien angeht. Das geht aber prinizipiell auch ohne CDN.

Und was kostet das?

Da es hier um Amazons Cloudfront gehen wird gibt darüber die Preisliste von Amazon Auskunft. Da kann man sich natürlich nicht wirklich viel drunter vorstellen, daher hier noch ein paar Zahlen aus der Praxis. Wir haben für den Erzgebirge-Palast alles auf dem CDN liegen: CSS-Dateien, Layoutbilder und Produktbilder. Unser Monatsmittel 2013 lag bei ca. $21, im vergangenen Jahr bei ca. $26. Für das Geld bekommt man ganz sicher nirgends ein vernünftiges Serverupgrade, mit dem man Lastspitzen wegpuffern kann. Und für uns, die einen sehr deutlichen Peak am Jahresende haben, ist das natürlich sehr wichtig.

Los geht’s: CDN-Distribution anlegen

Man braucht für das, was jetzt folgt, einen Amazon-AWS-Account und Zugriff auf seine Domainkonfiguration. Den Account kann man sich auf der Amazon-AWS-Seite anlegen.
Hat man das erledigt und auch eine Zahlungsweise hinterlegt kann man in der AWS Console ‘CloudFront’ auswählen. Nach dem Klick auf ‘Create Distribution’ wählen wir die Option ‘Web’, denn wir wollen ja Inhalte über HTTP und HTTPS ausliefern.

Jetzt geht es ans Ausfüllen des Formulars. Der einzige wichtige Eintrag ist Origin Domain Name. Hier tragen wir die URL unseres Shops ein (im Beispielfall ladeze.it) und nutzen somit automatisch Custom Origin, was vom Wartungsaufwand deutlich angenehmer ist als beispielsweise das Ausliefern der Dateien über Amazon S3.

Wer darüber hinaus eine regionale Einschränkung bei seinen Besuchern vornehmen kann, weil er beispielsweise gar nicht nach Asien verkauft, kann in den ‘Distribution Settings’ das bei ‘Price Class’ entsprechend auswählen. Wer eigene Domainnamen verwenden will statt abc.cloudfront.net kann diese bei ‘Alternate Domain Names (CNAMEs)’ angeben. Das machen wir mal und tragen cdn0.ladeze.it bis cdn5.ladeze.it dort ein, pro CNAME eine Zeile. Das bringt uns nämlich Vorteile wenn wir die Downloadbeschränkung der Browser umgehen wollen. Bei SSL müssen wir dann aber auf die von Amazon zur Verfügung gestellten URLs zurückgreifen, die Konfiguration mit einem eigenen SSL-Zertifikat würde den Rahmen dieses Artikels sprengen.

Nach dem Klick auf ‘Create Distribution’ startet Amazon das Deployment unserer neuen Distribution. Das dauert einen Moment und daher können wir die CNAME-Einträge für unsere Domains anlegen. Die Ziel-URL für den CNAME-Eintrag zeigt uns Amazon im Distribution-Dashboard bei ‘Domain Name’ an.

cdn-deployment

Wenn beides fertig ist, sowohl das Deployment der Distribution als auch das Einrichten der DNS-Einträge, können wir unser neues CDN zum ersten Mal testen. Das geht ganz einfach im Browser. Statt http://ladeze.it/images/product_images/thumbnail_images/53_0.jpg rufen wir aber http://cdn5.ladeze.it/images/product_images/thumbnail_images/53_0.jpg auf. Da DNS-Einträge bis zu 48 Stunden dauern können kann man, sollten die eigenen sich noch nicht herumgesprochen haben, den Test auch mit der von Amazon zur Verfügung gestellten URL durchführen und http://d4qtfj4qyflow.cloudfront.net/images/product_images/thumbnail_images/53_0.jpg aufrufen. Kommt das richtige Bild haben wir alles richtig gemacht.

Ein Blick in die Header-Angaben zeigt uns auch durch den vorhandenen Via-Record, dass die Datei von Cloudfront ausgeliefert wurde. Auch finden wir hier den X-Cache-Record. Beim ersten Aufruf steht dort ‘Miss from cloudfront’. Hier lag das Bild also nicht in Cloudfront vor und der Amazon-Server musste es von unserem Server laden. Alle subsequenten Aufrufe haben dann den Eintrag ‘Hit from cloudfront’.

cf-misscf-hit

Auch sieht man hier schön, dass unsere Einstellungen für max-age übernommen werden. Das Header-Plugin im Screenshot ist übrigens HTTP Headers.

Anpassungen am Shop

Jetzt geht es darum, dass unser modified-Shop die Bilder auch vom CDN ausliefert und nicht mehr von unserem Server.

Cache-Busting

Daten auf dem Cloudfront-CDN werden in der Default-Konfiguration erst nach 24 Stunden erneut auf Änderungen überprüft. Unsere .htaccess sagt sogar, dass Bilder 30 Tage im Cache gehalten werden können, ohne erneut zu prüfen, ob sich etwas verändert hat. Wir kommen also um ein Cache-Busting nicht herum. Dieses manuell zu machen wäre aber viel zu aufwendig. Daher betrifft die erste Änderung die Datei includes/classes/product.php, Zeile 492 wird dort ersetzt durch:

      $iTime = filemtime(DIR_FS_CATALOG.$path.$name);
      $aPath = pathinfo($name);
      return $path.$aPath['filename']."__".$iTime.".".$aPath['extension'];

Wir bauen also das letzte Modifikationsdatum mit in den Dateinamen ein und trennen es vom eigentlichen Namen mit zwei Unterstrichen ab. Völlig egal also ob wir über den Webbrowser oder direkt per SFTP ein Produktbild erneuern, es bekommt dann ein neues Modifikationsdatum und damit auch einen neuen Namen, der noch nicht im Cloudfront-CDN vorhanden ist und daher frisch angefordert wird.

Damit das auch funktioniert müssen wir außerdem im images/-Ordner eine .htaccess anlegen mit folgendem Inhalt:

RewriteEngine On
RewriteBase /
 
RewriteRule (.*)/(.*)__(.*).jpg$ images/$1/$2.jpg [L]

Weitere Änderungen am Core

Für das weitere Vorgehen braucht es eine Änderung in includes/configure.php, die wir nach dem define für HTTP_SERVER einfügen:

	define('HTTP_SERVER_CDN', 'http://cdn{i}.ladeze.it');
	define('HTTPS_SERVER_CDN', 'https://d4qtfj4qyflow.cloudfront.net');

Als nächstes modifizieren wir inc/xtc_image.inc.php.

Ersetzt wird

	// alt is added to the img tag even if it is null to prevent browsers from outputting
	// the image filename as default
	$image = '<img src="' . xtc_parse_input_field_data($src, array('"' => '&quot;')) . '" alt="' . xtc_parse_input_field_data($alt, array('"' => '&quot;')) . '"';

durch

	if(preg_match("/__/", $src)) {
		$aImage = pathinfo($src);
		$sSrc = preg_replace("/(.*)__(.*)/", "$1", $aImage['filename']);
		$sRealSrc = $aImage['dirname']."/".$sSrc.".".$aImage['extension'];
	}
	if(file_exists($src)) {
		$iCDN = (filesize($src)%5);
	} elseif(isset($sRealSrc) && file_exists($sRealSrc)) {
		$iCDN = (filesize($sRealSrc)%5);
	}
	if(REQUEST_TYPE!="SSL") {
		$sCDNSrc = str_replace("{i}", $iCDN, HTTP_SERVER_CDN)."/".$src;
	} else {
		$sCDNSrc = str_replace("{i}", $iCDN, HTTPS_SERVER_CDN)."/".$src;
	}
 
	// alt is added to the img tag even if it is null to prevent browsers from outputting
	// the image filename as default
	$image = '<img src="' . xtc_parse_input_field_data($sCDNSrc, array('"' => '&quot;')) . '" alt="' . xtc_parse_input_field_data($alt, array('"' => '&quot;')) . '"';

Damit werden jetzt schon diverse Bilder über das CDN geladen, aber vor allem die Produktbilder noch nicht. Um das zu erreichen braucht es ein Smarty-Plugin und Anpassungen an allen betroffenen Template-Dateien. Das kann in Arbeit ausarten, muss aber nur einmal gemacht werden. An dieser Stelle machen wir es nur anhand der Produktinfo-Seite.

Außerdem nutzen wir statt einer Domain für die Bilder jetzt mehrere. Da wir die Subdomain aufgrund des Modulos der Dateigröße festlegen ist es auch immer die gleiche Subdomain, die genutzt wird. Da Browser im Maximum sechs gleichzeitige Verbindungen zu einer Domain aufbauen haben wir damit die Anzahl der parallelen Zugriffe vervielfacht – und das CDN steckt das performancetechnisch locker weg.

Templates

Zunächst das Smarty-Plugin, dieses kommt nach templates/xtc5/smarty/function.cdn_image.php:

<?php
/**
 * Smarty plugin
 * @package Smarty
 * @subpackage plugins
 */
 
 
/**
 * Smarty {cdn_image} function plugin
 *
 * Type:     function<br>
 * Name:     cdn_image<br>
 * Purpose:  print an cdn_image()
 * @author Matthias Slovig
 * @param array parameters
 * @param Smarty
 * @return string|null
 */
function smarty_function_cdn_image($params, &$smarty) {
	require_once (DIR_FS_INC.'xtc_image.inc.php');
 
	return xtc_image($params['src'], $params['alt'], $params['width'], $params['height'], $params['params']);
}
 
/* vim: set expandtab: */
 
?>

Und dann die Anpassungen in templates/xtc5/module/product_info/product_info_tabs_v1.html:

<img src="{$PRODUCTS_IMAGE}" alt="{$PRODUCTS_NAME}" class="productimage" />

wird ersetzt durch

{cdn_image src=$PRODUCTS_IMAGE params="class='productimage'" alt=$PRODUCTS_NAME}

Das ist wie gesagt überall dort zu wiederholen, wo Bilder, die eigentlich über das CDN geladen werden sollen, bislang noch durch einen normalen img-Tag eingebunden sind, also Produktlistings, Kategorienlistings, Boxen etc.

CSS und Javascript

Zuletzt sind CSS und Javascript dran, dafür bedarf es auch zunächst eines Cache-Bustings, dieses Mal in der normalen .htaccess des Shops direkt nach der Defintion der RewriteBase:

  RewriteRule (.*)-([0-9]+)\.css$ $1.css [L]
  RewriteRule (.*)-([0-9]+)\.js$ $1.js [L]

Die neue templates/xtc5/css/general.css.php sieht so aus:

<?php
/* -----------------------------------------------------------------------------------------
   $Id: general.js.php 1262 2005-09-30 10:00:32Z mz $
 
   XT-Commerce - community made shopping
   http://www.xt-commerce.com
 
   Copyright (c) 2003 XT-Commerce
   -----------------------------------------------------------------------------------------
   Released under the GNU General Public License
   ---------------------------------------------------------------------------------------*/
 
   // Put CSS-Definitions here, these CSS-files will be loaded at the TOP of every page
 
	$iCDN['stylesheet'] = filesize(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/stylesheet-min.css")%5;
	$iCDN['thickbox'] = filesize(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/css/thickbox-min.css")%5;
	$iCDN['jquery-ui'] = filesize(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/css/jquery-ui.css")%5;
	$sURL['stylesheet'] = str_replace("{i}", $iCDN['stylesheet'], REQUEST_TYPE=='SSL' ? HTTPS_SERVER_CDN : HTTP_SERVER_CDN)."/templates/".CURRENT_TEMPLATE."/stylesheet-min-".filemtime(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/stylesheet-min.css").".css";
	$sURL['thickbox'] = str_replace("{i}", $iCDN['thickbox'], REQUEST_TYPE=='SSL' ? HTTPS_SERVER_CDN : HTTP_SERVER_CDN)."/templates/".CURRENT_TEMPLATE."/css/thickbox-min-".filemtime(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/css/thickbox-min.css").".css";
	$sURL['jquery-ui'] = str_replace("{i}", $iCDN['jquery-ui'], REQUEST_TYPE=='SSL' ? HTTPS_SERVER_CDN : HTTP_SERVER_CDN)."/templates/".CURRENT_TEMPLATE."/css/jquery-ui-".filemtime(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/css/jquery-ui.css").".css";
?>
<link rel="stylesheet" href="<?php echo $sURL['stylesheet'] ?>" type="text/css" />
<link rel="stylesheet" href="<?php echo $sURL['thickbox'] ?>" type="text/css" media="screen" />
 
<?php // BOF - web28 - 2010-07-09 - TABS/ACCORDION in product_info ?>
<?php
if (strstr($PHP_SELF, FILENAME_PRODUCT_INFO )) {
?>
<link rel="stylesheet" href="<?php echo $sURL['jquery-ui'] ?>" type="text/css" media="screen" />
<?php
}
?>
<?php // EOF - web28 - 2010-07-09 - TABS/ACCORDION in product_info ?>

Das Laden der CSS-Datei über das CDN führt automatisch dazu, dass auch alle relativ referenzierten Bilder ebenfalls über Cloudfront ausgeliefert werden.

Und so sieht die neue templates/xtc5/javascript/general.js.php aus:

<?php
/* -----------------------------------------------------------------------------------------
   $Id: general.js.php 1262 2005-09-30 10:00:32Z mz $
 
   XT-Commerce - community made shopping
   http://www.xt-commerce.com
 
   Copyright (c) 2003 XT-Commerce
   -----------------------------------------------------------------------------------------
   Released under the GNU General Public License
   ---------------------------------------------------------------------------------------*/
 
 
   // this javascriptfile get includes at the BOTTOM of every template page in shop
   // you can add your template specific js scripts here
 
	$iCDN['jquery'] = filesize(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/javascript/jquery.js")%5;
	$iCDN['thickbox'] = filesize(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/javascript/thickbox.js")%5;
	$iCDN['jquery-ui'] = filesize(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/javascript/jquery-ui.js")%5;
	$sURL['jquery'] = str_replace("{i}", $iCDN['stylesheet'], REQUEST_TYPE=='SSL' ? HTTPS_SERVER_CDN : HTTP_SERVER_CDN)."/templates/".CURRENT_TEMPLATE."/javascript/jquery-".filemtime(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/javascript/jquery.js").".js";
	$sURL['thickbox'] = str_replace("{i}", $iCDN['thickbox'], REQUEST_TYPE=='SSL' ? HTTPS_SERVER_CDN : HTTP_SERVER_CDN)."/templates/".CURRENT_TEMPLATE."/javascript/thickbox-".filemtime(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/javascript/thickbox.js").".js";
	$sURL['jquery-ui'] = str_replace("{i}", $iCDN['jquery-ui'], REQUEST_TYPE=='SSL' ? HTTPS_SERVER_CDN : HTTP_SERVER_CDN)."/templates/".CURRENT_TEMPLATE."/javascript/jquery-ui-".filemtime(DIR_FS_CATALOG."templates/".CURRENT_TEMPLATE."/javascript/jquery-ui.js").".js";
?>
 
<script src="<?php echo $sURL['jquery'] ?>" type="text/javascript"></script>
<script src="<?php echo $sURL['thickbox'] ?>" type="text/javascript"></script>
 
<?php // BOF - web28 - 2010-07-09 - TABS/ACCORDION in product_info ?>
<?php
if (strstr($PHP_SELF, FILENAME_PRODUCT_INFO )) {
?>
<script src="<?php echo $sURL['jquery-ui'] ?>" type="text/javascript"></script>
<script type="text/javascript">
/* <![CDATA[ */
	//Laden einer CSS Datei mit jquery
    $.get("<?php echo 'templates/'.CURRENT_TEMPLATE; ?>"+"/css/javascript.css", function(css) {
		$("head").append("<style type='text/css'>"+css+"<\/style>");
	});
 
	$(function() {
		$("#tabbed_product_info").tabs();
		$("#accordion_product_info").accordion({ autoHeight: false });
	});
/*]]>*/
</script>
<?php
}
?>
<?php // EOF - web28 - 2010-07-09 - TABS/ACCORDION in product_info ?>

Optimizing JPEG images – save 20% in file size

Optimizing your image file size is one of the low hanging fruits when it comes to page speed optimization for your website.

My current workflow for JPEGs looks something like this:

  • ‘Save for Web’ in Photoshop with 60% quality
  • Run through ImageOptim to reduce file size

Yesterday, Marc Thiele asked on Twitter:

In the unfolding discussion some folks stated that they use both JPEGmini and ImageOptim when it comes to JPEGs and that this would reduce the file size even more. Fortunately I had to add pictures to 50 new products today. Every product has three pictures from different angles, so this sums it up to 150 images in various sizes, 900 images in total, all JPEG.
So I gave it a try and here are the results (I bought JPEGmini for this test as 20 images on the lite version might not be enough to get reliable results, still, your mileage may vary).

‘Save for Web’

With just using ‘Save for Web’ the images had a total size of 84MB. Quite heavy.
photoshop

ImageOptim

Running these through ImageOptim saved nearly 10% and lowered the total size to 75.7MB.
imageoptim

JPEGmini

Using the ‘Save for Web’ images again (not touched by ImageOptim yet) results in 74.2MB.
jpegmini

ImageOptim after JPEGmini

Now adding another run of ImageOptim to the images already optimized by JPEGmini lowers the total size to 66.9MB. Thats more than 20% in file size saved without compromising image quality.
jpegmini-imageoptim

As far as I can tell it is not necessary to run the tools multiple times. At least in ImageOptim it is obvious that the latest version does this by itself. And I wasn’t able to spot any major changes on a second and third run with JPEGmini.

To give you the ability to compare the results, below are two images. The left one is the image just saved from Photoshop, the right one is the image after the whole optimization process.
photographer_ps photographer_optimized

Der schnellste Erzgebirge-Palast, den es je gab.

Schnell ist der Erzgebirge-Palast ja eigentlich schon immer. Also spätestens seit Ende 2008, wo zum ersten Mal der Server etwas geschwächelt hat und ich viel optimiert habe. Seit dieser Zeit kommen auch die statischen Inhalte größtenteils von Amazons Cloudfront CDN. In den letzten Jahren hat es der Shop mit diesen Optimierungen in Google’s PageSpeed auf einen Wert von 95 (100 ist der Maximalwert) gebracht.

Ich habe auch viel dazu geschrieben, wie man mit einfachen Mitteln ähnliche Werte erreichen kann. Was ich nie erwähnt habe ist die Sache mit dem CDN, weil das schon eher Highlevel ist und weil mein bisheriger Workflow, Cloudfront war damals quasi frisch geschlüpft und konnte noch nicht wirklich viel, doch sehr umständlich ist:
Inhalte mussten auf Amazon S3 liegen, was man zwar mit einem Tool wie s3sync mehr oder weniger automatisieren kann, aber aufwendig ist es trotzdem. Und da ich lange Expire-Header nutze musste ich beispielsweise dem CSS immer ein Datum mitgeben, das ganze syncen und schließlich die Referenz im <head> auch noch anpassen. Nichts, was man irgendwem raten möchte, nachzubauen.

Beim Blättern in den Folien von Christian Schäfer kam mir dann der Gedanke, dass ich mir ja grad mal das PageSpeed-Resultat vom Shop anzeigen lassen könnte. Weiterhin 95, aber ein Punkt tauchte dort auf, der mich eigentlich schon immer nervt, seit ich auf das CDN umgestellt habe: Die CSS-Datei ist nicht gzip-komprimiert. Und wir reden hier von 65kB vs. 12kB! Aus dem Grund hatte ich da auch schon mal versucht, was zu bauen, was aber nie wirklich funktioniert hat (Datei bereits gezippt ablegen und mit entsprechendem Zusatzheader ausliefern). Kurz gegoogelt und darauf gestoßen, dass Cloudfront schon seit einiger Zeit ‘custom origin’ unterstützt. Das hat zwei Vorteile:

  1. Die Inhalte müssen nicht mehr wie bisher auf S3 liegen sondern können von einem beliebigen Webserver kommen.
  2. Header, die der Original-Webserver schickt, werden von Cloudfront genau so weitergegeben.

Man braucht dazu nur zwei Dinge:
Eine Domain, die cookieless arbeitet. Also einen vhost einrichten, der das selbe DocumentRoot hat wie der eigentliche Webauftritt und diesem sagen, dass er sämtliche Cookies verwerfen soll: Header unset Cookie.
Mit dieser Domain, nennen wir sie mal static.example.org, legen wir jetzt eine neue Distribution in Cloudfront an, geben an, dass die Header so übernommen werden sollen, wie sie von der Quelle kommen, warten einen Moment, bis das alles deployed ist und können das ganze dann wie bisher nutzen. Statt von S3 holt Cloudfront jetzt aber die Daten von unserem Server. Das passiert auch nur genau beim ersten Request, denn ab dann hat Amazon das ganze ja im Cache.

Damit kann man Cloudfront nicht nur mit gzip nutzen, sondern sogar mit mod_rewrite, denn die eigentlichen RewriteRules werden ja auf dem eigenen Server ausgeführt. Das eröffnet völlig neue Möglichkeiten und so hat mein CSS keinen von Hand eingetragen Datumsteil mehr, sondern einen dynamisch ausgelesenen Zeitstempel des letzten Modifikationsdatums:
http://cdn6.wstatic.com/templates/erzgebirge/styles-yui-min-1350656989.css

Wer sich übrigens dafür interessiert, wie ich das für xt:Commerce umgesetzt habe, dass die statischen Inhalte über Cloudfront ausgeliefert werden, möge dies bitte in den Kommentaren kundtun. Dann kann ich das auch mal in einem Artikel entsprechend aufbereiten.

Was das ganze gebracht hat sind zwei weitere Punkte auf der Skala: 97 von 100. Der Rest liegt nicht mehr wirklich in meiner Macht, außer, ich würde Google Analytics, olark und die SSL-Logos rauswerfen.

Hinten anstellen – xtcModified on steroids

Man kennt das aus dem Supermarkt: An der Kasse mal eben vordrängeln, um dann den Betrieb komplett aufzuhalten, weil man 14,83 Euro passend abgezählt in aller Seelenruhe aus dem Geldbeutel kramt. Gerne noch gepaart mit: „Ich hou mei Brilln daham vergessn, guggn sa amol, sinn des fünf odder zwaa Zent?”. Gibt’s bei der Geschwindigkeitsoptimierung im Web auch. Heißt da aber nicht Oma Meier sondern Javascript.

Javascript später parsen

Parsen Sie Javascript später, um die Blockierung der Seitendarstellung zu reduzieren.

Weil Javascript Inhalt und DOM der Seite verändern kann warten die Browser, sobald sie auf einen Script-Tag stoßen, bis das Javascript geladen, geparsed und gerendert ist, bevor sie mit dem eigentlichen Rendering der Seite fortfahren. Durch das Mischen von Javascript- und CSS-Tags kann das Laden noch einmal zusätzlich verlangsamt werden, Page Speed listet das unter ‘Reihenfolge der Formate und Skripts optimieren’. Eine Grafik, wie diese Verzögerung eben durch das Stoppen des eigentlichen Rendervorgangs aussehen kann, findet sich bei Google.

Heute geht es also darum, die Reihenfolge von CSS und Javascript im Hinblick auf Ladezeiten zu optimieren. Dabei gilt die Faustregel, CSS gleich zu Beginn und Javascript ganz am Ende zu laden. Allerdings muss man beachten, dass es Fälle gibt, bei denen man von dieser Regel abweichen muss, weil man Javascript-Funktionen schon vorher benötigt. Hat man beispielsweise einen SSL-Proxy und muss Cross-Domain-Tracking mittels Google Analytics implementieren, benötigt man das Analytics-Javascript vorher und kann es eben nicht ganz ans Ende verfrachten.
Auch die eigentliche Reihenfolge der Scripte ist wichtig, erfordern manche Funktionen bestimmte Bibliotheken, z.B. jQuery, muss dieses natürlich vorher geladen werden. Und vertauscht man die Reihenfolge bei CSS-Dateien könnte es aufgrund der Kaskade zu unerwarteten Darstellungsfehlern kommen.

Im Sourcecode von xtcModified finden sich Hinweise, dass man dieses Thema auch schon mal angegangen ist, aufgrund von Fehlern bei jQuery wurde das aber wieder rückgängig gemacht. Aktuell konnten ich bei Tests im Demoshop keine derartigen Probleme feststellen. In xtcModified findet sich außerdem noch sehr viel Javascript-Code, der nicht über eine externe Datei geladen wird. Hier aufzuräumen und den Code, der zum Teil noch aus dem originalen xt:Commerce stammt, gegen moderne, auf jQuery basierende Funktionen zu tauschen, würde eine eigene Serie gut füllen.

Daher beschränke ich mich hier mal auf das naheliegendste: In includes/header.php alles von Zeile 75 bis Zeile 278 in die Zwischenablage kopieren und dann löschen, ebenso Zeile 279 löschen. In includes/application_bottom.php die Zeilen 42 bis 46 markieren:

//BOF - DokuMan - 2010-02-25 - Enhance page loading time by putting CSS on TOP of page and JavaScript on BOTTOM of page
//BOF - web28 - 2010-07-14 -  change to TOP of page again because jquery view problems
//require('templates/'.CURRENT_TEMPLATE.'/javascript/general.js.php');
//EOF - web28 - 2010-07-14 -  change to TOP of page again because jquery view problems
//EOF - DokuMan - 2010-02-25 - Enhance page loading time by putting CSS on TOP of page and JavaScript on BOTTOM of page

und durch

?>

ersetzen. Anschließend den Inhalt der Zwischenablage einfügen.

Betrachtet man die reinen Zahlen könnte man meinen, diese Änderung bringt uns primär ein gutes Gefühl.

YSlow mit Javascript am Seitenende

Page Speed mit Javascript am Seitenende

Der eigentliche Erfolg ist hier aber eher offensichtlich, wenn man sich das Rendering ansieht. Mit Javascript im oberen Bereich der Seite wird der Browser sehr früh blockiert und beginnt erst nach ca 2,5 Sekunden mit dem Rendern. Hingegen beginnt dieser Prozess nach der Änderung gut eine Sekunde früher.

Weitere Artikel der Serie

  1. Höher, schneller, weiter
  2. Verfallsdatum überschritten
  3. Druck machen – xtcModified on steroids

Disclaimer: Ich bin nach wie vor der Meinung, dass viele der Einstellungen in der Serverkonfiguration besser aufgehoben sind als in der .htaccess-Datei, da aber sicherlich viele das ganze auch auf Shared Hosting umsetzen möchten werden alle Änderungen, die auch über die .htaccess durchgeführt werden können, im Rahmen dieser Artikelreihe dort vorgenommen.

Druck machen – xtcModified on steroids

Diesmal wollen wir im Rahmen der Artikelserie zur Geschwindigkeitsoptimierung mal ein bisschen Druck ausüben, denn die Steigerung von 36 auf 60 Punkten ist bestenfalls ein Teilerfolg.

Komprimierung aktivieren

Die Komprimierung der folgenden Ressourcen mit gzip könnte ihre Übertragungsgröße um 90.0 KiB verringern (Reduzierung um 70%).

70% der zu übertragenden, komprimmierungsfähigen Daten könnte man sich also sparen, wenn man diese vor der Auslieferung zippt. Das Entpacken findet dann beim Empfänger durch den Browser transparent statt. Jeder moderne Browser kann damit umgehen. Will man es auf die Goldwaage legen könnte man argumentieren, dass der Browser durch den erforderlichen Entpackvorgang länger braucht, um die Seite zu rendern. Aber das Nadelöhr ist nach wie vor ganz klar die Bandbreite der Leitung zum Empfänger, während die Prozessoren in moderner (Desktop-)Hardware gerade mal müde lächeln, wenn sie komprimierte Daten vorgesetzt bekommen.
Für die meisten eher unbedeutend ist in Zeiten von Traffic-Flatrates auch für Server die Einsparung beim Datenvolumen. Aber spätestens beim Thema CDN, wo nach wir vor nach Traffic abgerechnet wird, wird es plötzlich wieder interessant.

Komprimierung? Bringt der Shop doch schon mit!

php bietet von Haus aus die Möglichkeit, Daten komprimiert auszuliefern und in xtcModified ist die Option im Admin unter ‘Erweiterte Konfiguration’ – ‘Gzip Kompression’ auch schnell gefunden und aktiviert. Nur: Das gelbe Ausrufezeichen in Page Speed verschwindet nicht, die Note von YSlow für die Komprimierung wird nur marginal besser.

Der Grund ist einfach: Die shopeigene Komprimierung wird wie gesagt vom php-Parser zur Verfügung gestellt. Nur Dateien, die diesen vor Auslieferung passieren, werden komprimiert. Und genau das gilt nicht für CSS und Javascript. Aber gerade hier findet sich ein enormes Einsparpotential.

mod_deflate kümmert sich um alles

Auch hier kommt uns wieder ein Apache-Modul zu Hilfe: mod_defalte (seit Apache 2, davor hieß es mod_gzip und wer noch einen Apache 1 im Einsatz hat sollte seinen Serveradmin mal fragen, ob er nicht lieber mit etwas Geld verdienen möchte, womit er sich auch auskennt). Und auch hier lohnt ein Blick in die .htaccess des HTML5Boilerplate. Zuvor schalten wir aber die shopeigene Komprimierung wieder aus, sie wird nicht benötigt.

<IfModule mod_deflate.c>
 
# Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
<IfModule mod_setenvif.c>
  <IfModule mod_headers.c>
    SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)s*,?s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
    RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
  </IfModule>
</IfModule>
 
# HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
<IfModule filter_module>
  FilterDeclare   COMPRESS
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/html
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/css
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/plain
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/x-component
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/javascript
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/json
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xhtml+xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/rss+xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/atom+xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/vnd.ms-fontobject
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/svg+xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/x-font-ttf
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $font/opentype
  FilterChain     COMPRESS
  FilterProtocol  COMPRESS  DEFLATE change=yes;byteranges=no
</IfModule>
 
<IfModule !mod_filter.c>
  # Legacy versions of Apache
  AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
  AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
  AddOutputFilterByType DEFLATE image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype
</IfModule>
</IfModule>

Zunächst kümmert man sich um Clients, die aufgrund von Proxies oder anderer Einflüsse kaputte Header schicken und standardmäßig nicht mit komprimierten Inhalten versorgt werden würden. Gemäß einem Blogpost im Yahoo! Developer Network sind das ca. 15% der Besucher, also eine nicht zu unterschätzende Anzahl.

Anschließend werden mit Hilfe von mod_filter, das wesentlich flexibler ist als das früher benutzte AddOutputFilterByType, Inhalte in Abhängigkeit ihres MIME-Type (den wir ja schon im zweiten Teil gerade gezogen haben) komprimiert. Bei Bildern beispielsweise wäre das nur verschwendete Rechenleistung. Der letzte Block ist für Server, die ohne mod_filter laufen, indem die ‘alte’ mod_deflate-Syntax zum Einsatz kommt.

Wer jetzt neu lädt wird sich wundern, dass sich nach wie vor nichts tut, unsere Javascript- und CSS-Daten noch immer bemängelt werden. Nachdem wir aber bereits im vorherigen Teil die Expire-Zeiten hoch gesetzt haben fragt der Browser gar nicht mehr beim Server nach, sondern holt die Datei aus seinem Cache und die ist natürlich unkomprimiert.
Um realistische Ergebnisse aus Page Speed zu bekommen müssen wir also den Browsercache umgehen, entweder indem wir ihn leeren oder indem wir mit gedrückter Hochstelltaste auf das ‘Neu laden’-Icon klicken – zumindest in Firefox und Chrome führt das zum gewünschten Ergebnis. Das ist aber auch nur deshalb praktikabel, weil sich unser Shop noch im Testbetrieb befindet. Wie man das Problem im Livebetrieb umgeht haben wir ja schon behandelt.

YSlow jetzt mit der Gesamtnote A

YSlow jetzt mit der Gesamtnote A

Auch bei Page Speed die 75%-Marke überschritten

Auch bei Page Speed die 75%-Marke überschritten

Natürlich gibt es auch die neue .htaccess mit dem aktuellen Status zum Dowload.

Weitere Artikel der Serie

  1. Höher, schneller, weiter
  2. Verfallsdatum überschritten

Disclaimer: Ich bin nach wie vor der Meinung, dass viele der Einstellungen in der Serverkonfiguration besser aufgehoben sind als in der .htaccess-Datei, da aber sicherlich viele das ganze auch auf Shared Hosting umsetzen möchten werden alle Änderungen, die auch über die .htaccess durchgeführt werden können, im Rahmen dieser Artikelreihe dort vorgenommen.

Verfallsdatum überschritten – xtcModified on steroids

36 von 100 möglichen Punkten erreicht ein frisch installierter xtcModified auf einem 1&1 Virtual Server. Diesen Wert gilt es jetzt im Rahmen dieser Artikelserie zu optimieren.

Browser-Caching nutzen

Ganz oben auf der Liste von Page Speed steht das fehlende Browser Caching.

Die Aktualität der folgenden Cache-fähigen Ressourcen ist nur von kurzer Dauer. Legen Sie fest, dass folgende Ressourcen künftig mindestens einmal pro Woche ablaufen:

Und darunter kommt so ziemlich jedes Bild, das Stylesheet und die Javascript-Dateien, die alle kein Ablaufdatum haben. Dieses würde festlegen, wie lange der Browser oder auch ein Proxyserver die Datei als aktuell betrachtet. Mit der aktuellen Konfiguration fordert er jede Datei bei jedem Aufruf erneut an, denn kein Ablaufdatum bedeutet, dass die Datei aus Browsersicht sofort veraltet ist.

Ein solches Verhalten ist sinnvoll und wünschenswert bei Inhalten, die bei jeder Anforderung die aktuellen Gegebenheiten reflektieren müssen, so führt beispielsweise das hinzufügen eines Artikels zum Warenkorb zu einer Änderung und das HTML für den Warenkorb darf dann eben nicht aus dem Cache geholt werden. Bilder, Stylesheets und Scripts ändern sich aber in der Regel nicht mit jedem Klick. Es ist also völlig unnötig, diese Dateien jedes Mal erneut anzufordern bzw. auch nur beim Server anzufragen, ob sich was geändert hat.

mod_expires for the rescue

Zwar gibt es in der .htaccess-Datei, die mit xtcModified mitkommt, einen Eintrag, um bestimmte Formate mit einem Ablaufdatum von einem Monat zu versehen (der aber standardmäßig nicht aktiv ist), ich bevorzuge hier aber die feiner aufgranulierte Lösung aus dem HTML5Boilerplate:

<IfModule mod_expires.c>
  ExpiresActive on
 
# Perhaps better to whitelist expires rules? Perhaps.
  ExpiresDefault                          "access plus 1 month"
 
# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
  ExpiresByType text/cache-manifest       "access plus 0 seconds"
 
# Your document html 
  ExpiresByType text/html                 "access plus 0 seconds"
 
# Data
  ExpiresByType text/xml                  "access plus 0 seconds"
  ExpiresByType application/xml           "access plus 0 seconds"
  ExpiresByType application/json          "access plus 0 seconds"
 
# Feed
  ExpiresByType application/rss+xml       "access plus 1 hour"
  ExpiresByType application/atom+xml      "access plus 1 hour"
 
# Favicon (cannot be renamed)
  ExpiresByType image/x-icon              "access plus 1 week" 
 
# Media: images, video, audio
  ExpiresByType image/gif                 "access plus 1 month"
  ExpiresByType image/png                 "access plus 1 month"
  ExpiresByType image/jpg                 "access plus 1 month"
  ExpiresByType image/jpeg                "access plus 1 month"
  ExpiresByType video/ogg                 "access plus 1 month"
  ExpiresByType audio/ogg                 "access plus 1 month"
  ExpiresByType video/mp4                 "access plus 1 month"
  ExpiresByType video/webm                "access plus 1 month"
 
# HTC files  (css3pie)
  ExpiresByType text/x-component          "access plus 1 month"
 
# Webfonts
  ExpiresByType font/truetype             "access plus 1 month"
  ExpiresByType font/opentype             "access plus 1 month"
  ExpiresByType application/x-font-woff   "access plus 1 month"
  ExpiresByType image/svg+xml             "access plus 1 month"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
 
# CSS and JavaScript
  ExpiresByType text/css                  "access plus 1 year"
  ExpiresByType application/javascript    "access plus 1 year"
 
  <IfModule mod_headers.c>
    Header append Cache-Control "public"
  </IfModule>
 
</IfModule>

Das ganze in If-Bedingungen zu verpacken verhindert effektiv Internal Server Errors (500), wenn das Modul nicht installiert ist. Klar, dass das ganze dann auch nichts bringt, aber ein Shop, der Inhalte ohne die Optimierung ausliefert ist immer noch besser als ein Shop, der nur eine Fehlermeldung zurückgibt.
Wenn aber mod_expires aktiv ist geschieht folgendes: Der generelle Wert für das Ablaufdatum wird auf einen Monat gesetzt und das ganze dann für Inhalte, die nicht gecached werden sollen, wieder zurückgesetzt. Anschließend werden die Werte für einzelnen Formate basierend auf dem MIME-Type gesetzt. Man könnte alles weglassen, was dem generellen Wert entspricht, aber ich lasse das hier mal mit aufgeführt, vielleicht will man den ein oder anderen Ablaufzeitraum doch anpassen, bei X-Skating sind es beispielsweise 10 Jahre für Bilder. Auf die dabei auftretenden Probleme gehe ich später noch ein.
Cache-Control “public” sorgt dann noch dafür, dass diese Inhalte auch von Proxies gecached werden können, auch wenn man sich vorher per HTTP authentifizieren musste (was für xtcModified aber nicht zutrifft).

Da mod_expires auf Grundlage der MIME-Types arbeitet empfiehlt es sich, noch einen anderen Bereich aus der Datei von HTML5Boilerplate zu übernehmen, nämlich den Teil, wo die MIME-Types vereinheitlicht werden, gerade Javascript kommt je nach Serverkonfiguration mit den obskursten MIME-Types daher:

# JavaScript
#   Normalize to standard type (it's sniffed in IE anyways)
#   tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript         js
 
# Audio
AddType audio/ogg                      oga ogg
AddType audio/mp4                      m4a
 
# Video
AddType video/ogg                      ogv
AddType video/mp4                      mp4 m4v
AddType video/webm                     webm
 
# SVG
#   Required for svg webfonts on iPad
#   twitter.com/FontSquirrel/status/14855840545
AddType     image/svg+xml              svg svgz
AddEncoding gzip                       svgz
 
# Webfonts
AddType application/vnd.ms-fontobject  eot
AddType application/x-font-ttf         ttf ttc
AddType font/opentype                  otf
AddType application/x-font-woff        woff
 
# Assorted types
AddType image/x-icon                        ico
AddType image/webp                          webp
AddType text/cache-manifest                 appcache manifest
AddType text/x-component                    htc
AddType application/x-chrome-extension      crx
AddType application/x-opera-extension       oex
AddType application/x-xpinstall             xpi
AddType application/octet-stream            safariextz
AddType application/x-web-app-manifest+json webapp
AddType text/x-vcard                        vcf

Die einzige Änderung, die wir noch an den Expires vornehmen ist das Favicon, das wird ebenfalls auf einen Monat gesetzt. Die .htaccess, wie sie bis zu diesem Zeitpunkt aussieht, kann hier heruntergeladen werden.

Erneut durch YSlow und Page Speed überprüft ergibt sich schon ein wesentlich besseres Bild. YSlow pendelt sich jetzt bei 89 Punkten ein, noch deutlicher ist die Steigerung bei Page Speed, 60 von 100 möglichen Punkten stehen jetzt bereits zu Buche.

YSlow klettert von 80 auf 89 Punkte...


...Page Speed sogar von 36 auf 60

Auch schön zu sehen ist die extreme Reduktion an HTTP-Requests durch diese Maßnahme. So zeigt das Wasserfalldiagramm bei webpagetest.org ohne mod_expire auch bei der Anfragewiederholung noch alle Bilder etc. Der Server antwortet zwar mit 304 Not Modified, aber die Anfrage geht trotzdem raus und muss beantwortet werden. Sind Expire-Header gesetzt gehört das der Vergangenheit an.

Inhalte aktualisieren bei langen Expires

(Sehr) lange Expire-Zeiten bringen ein Problem mit sich: Da der Browser davon ausgeht, dass die CSS-Datei, die er gestern geladen hat, noch ein Jahr gültig ist, wird er erst gar nicht auf die Idee kommen, diese neu anzufordern. Daher kommt man um eine Versionierung nicht herum. HTML5Boilerplate empfiehlt das über Query-Paramter zu machen, statt stylesheet.css ruft man also beispielsweise stylesheet.css?20120112 auf. Damit man das nicht jedes Mal händisch aktualisieren muss sollte man die Zeilen 15 und 16 in templates/xtc5/css/general.css.php wie folgt ändern:

<link rel="stylesheet" href="<?php echo 'templates/'.CURRENT_TEMPLATE; ?>/stylesheet.css?<?php echo filemtime('templates/'.CURRENT_TEMPLATE.'/stylesheet.css') ?>" type="text/css" />
<link rel="stylesheet" href="<?php echo 'templates/'.CURRENT_TEMPLATE; ?>/css/thickbox.css?<?php echo filemtime('templates/'.CURRENT_TEMPLATE.'/css/thickbox.css') ?>" type="text/css" media="screen" />

Damit wird immer das Datum der letzten Modifikation als Unix-Zeitstempel angehangen. Das gleiche sollte man auch für das Javascript machen, aber da wir uns dem eh im Rahmen dieser Serie noch gesondert annehmen müssen verschieben wir das auf einen späteren Zeitpunkt.

Als letztes Problemkind bleiben die Bilder. Die Lösung dafür ist etwas aufwendiger und wird behandelt werden wenn wir auf das Thema Content Delivery Networks (CDN) zu sprechen kommen.

Weitere Artikel der Serie


Disclaimer: Ich bin nach wie vor der Meinung, dass viele der Einstellungen in der Serverkonfiguration besser aufgehoben sind als in der .htaccess-Datei, da aber sicherlich viele das ganze auch auf Shared Hosting umsetzen möchten werden alle Änderungen, die auch über die .htaccess durchgeführt werden können, im Rahmen dieser Artikelreihe dort vorgenommen.

Höher, schneller, weiter – xtcModified on steroids

So langsam erreicht die Erkenntnis, dass Websitegeschwindigkeit (auch) ein Rankingkriterium sein kann, auch die kleineren und ganz kleinen Shops.

Zeit, in einer Artikelserie mal zu zeigen, wie man aus einem der derzeit beliebtesten Open-Source-Systeme, nämlich aus xtcModified, das Maximum rauskitzeln kann.

Ausgangssituation

Shopsystem

Es beginnt mit einem xtcModified, wie er aus der Schachtel fällt. Zum Zeitpunkt dieser Artikelserie trägt er den schönen und leicht zu merkenden Namen 1.05 SP1b. In diesen wurden die Demoartikel importiert, allerdings mit individuellen Artikelnummern, damit es mehr als vier unterschiedliche Produkte sind. Außerdem wurden noch SEO-URLs aktiviert und vier Artikel auf die Startseite gestellt.

Hosting

Installiert ist der Shop auf einem 1&1 Virtual Server L, denn nur so hat man die maximale Kontrolle. Das sollte man im Hinterkopf behalten, wenn man die im Rahmen dieser Serie vorgestellten Tipps auf einem Shared Hosting umsetzen will, manche Sachen gehen dort einfach nicht.

Domain

Ach ja, Domains kauft man nicht, Domains hat man. Es stimmt, dass man die bei eigentlich jedem Hosting hinterherworfen bekommt. Trotzdem sollte man darauf achten, dass man eine größtmögliche Flexibilität bei der Konfiguration hat.
10 Subdomains, wie es 1&1 beispielsweise früher für einen Server angeboten hat, sind eine absolut sinnlose künstliche Limitation. Domaindiscount24 hat sich in den vergangenen Jahren als zuverlässiger Partner erwiesen was das Domainhosting angeht. Wer keinen eigenen Server hat sollte aber vorher mit seinem Hoster klären, ob dieser Fremddomains einrichten kann. Bei den meisten geht das kostenlos, bei manchen muss man eine geringe monatliche Gebühr einplanen.

Tools

Yahoos YSlow und Googles Page Speed sind sicherlich die beiden bekanntesten Browser-Plugins, die sich dem Thema Websitegeschwindigkeit widmen. Diese beiden werden uns im Rahmen der Serie begleiten, hin und wieder aber vom ein oder anderen Online-Tool ergänzt werden. Die Kritierien, die beide überprüfen, ähneln sich, die Ergebnisse könnten aber unterschiedlicher nicht sein.

Ausgangssituation YSlow

Während YSlow 80 Punkte vergibt und damit auf die amerikanische Schulnote ‘B’ kommt kann sich Page Speed bei unserem Testshop unter www.ladeze.it lediglich für 36 von 100 Punkten erwärmen, was zu einem roten Gesamtwert führt.

Ausgangssituation PageSpeed

Damit konfrontiert geht es jetzt an die Optimierung, denn auch beim ‘B’ von YSlow ist deutlich Optimierungspotential zu sehen, gibt es doch für den ein oder anderen Punkt eine glatte Sechs.

Wer sich die Wartezeit bis zum zweiten Teil etwas verkürzen will sollte in den Podacst von OnlineRader reinhören. Der bietet einen guten Überblick zum Thema.


Disclaimer: Ich bin nach wie vor der Meinung, dass viele der Einstellungen in der Serverkonfiguration besser aufgehoben sind als in der .htaccess-Datei, da aber sicherlich viele das ganze auch auf Shared Hosting umsetzen möchten werden alle Änderungen, die auch über die .htaccess durchgeführt werden können, im Rahmen dieser Artikelreihe dort vorgenommen.

Mach ma kleiner!

An allen Ecken und Enden wird optimiert, was das Zeug hält. Bei Javascript und CSS gehört es quasi mittlerweile zum guten Ton, das ganze um Leerzeichen und Kommentare bereinigt auszuliefern, um die Dateigröße zu minimieren und damit die Ladezeiten zu optimieren. Bei Bildern, so scheint es, ist diese Erkenntnis noch nicht überall angekommen. Aber auch hier steckt immenses Optimierungspotential.

Zwei Apps erleichtern dem Webworker dabei die tägliche Arbeit.

Smaller

Sicher, es gibt diverse Online-Lösungen, die ein unkomprimiertes Javascript oder CSS durch den YUI Compressor jagen und man kann sich diesen natürlich auch selbst installieren, aber eine grafische Oberfläche, die auch Dateien per Stapelverarbeitung abarbeiten kann, wäre schon schön. Genau das leistet die Shareware Smaller, die darüber hinaus auch noch HTML komprimiert. Mit $20 nicht ganz billig, aber ich möchte im täglichen Arbeitsablauf nicht mehr darauf verzichten müssen. Neben der reinen Komprimierung kann das Programm darüber hinaus auch noch mehrere Dateien zu einer kombinieren.

ImageOptim

Eine verlustfreie Komprimierung von Bildern hat sich dagegen ImageOptim auf die Fahnen geschrieben. Auch hier ist eine Stapelverarbeitung möglich, das Tool unterstützt alle gängigen Web-Grafikformate (inkl. animierterter GIFs, sofern das heutzutage noch jemand braucht) und wählt selbständig die beste Kompressionsmethode. Selbst aus Bilder, die in Photoshop mit „Für Web speichern” gespeichert wurden kitzelt es noch ein paar Prozent raus. Punkten kann das Tool auch beim Preis, es ist nämlich Freeware.

Kleinere Dateien wirken sich nicht nur bei der Ladezeit positiv aus sondern mitunter auch im Geldbeutel, beispielweise wenn man seine Bild- und CSS-Dateien über ein CDN ausliefern lässt.