Rckt site performance testing and optimisation

With the launch of the new Rckt site (nice, isn't it?), and the recent release of a newer, faster, shinier MODX revolution, we thought it would be fun to see how fast our site is, and just how fast it's possible to make it.
Short version
With a lot of hard work, we managed to half the time it took to load the homepage. If you've no technical interest in how we did it, stop reading now.
Much longer, much more technical version
Still reading? Sit down, get comfy, grab a cup of tea - this is a long but hopefully informative read.
Site optimisation is regularly talked about. There's a lot of articles out there which go into great detail about what can be done to improve the performance of a website. But really, how much hard work is it to do and is it really worth it?
Also, will any of the changes make any difference to the perceived loading speed of the site? From a user's point of view does it feel any quicker, or is it purely in the numbers?
The Plan
We'll run baseline tests and investigate what can be done to try and improve on those "scores".
First, we need to investigate what the most viewed entry pages to the Rckt site are using Google Analytics. This will allow us to determine which pages should be tested.
We'll only consider what can be changed easily without interruption or downtime, what should be changed, and what we think we should avoid.
Every time we change something, we'll collect further results showing both the average times and variation/standard deviation. This will help point out anomalous results which are likely due to fluctuations in network performance, etc.
Firebug is a brilliant tool which has a nifty built feature for network "waterfall" graphs. It allows us to run a report which tells us in great detail loading, network waiting times and various other useful information.
We will perform five page loads from empty (cold) cache, followed by five page loads with hot cache. This should give us a good indication of performance differences between first time visitors and those who view more than one page in a particular time period.
Most viewed entry pages
The most viewed "landing" pages at the time of writing were the home page and the most recent news article.
Baseline Results
Home page
The baseline page size was 26 HTTP requests and a combined filesize of 442.2KB
- Image: 16 items, 262.8KB
- Javascript: 4 items, 70.3KB
- CSS: 2 items, 28.2KB
- HTML: 1 item, 8KB

With a hot cache, the page loads around 434.2KB from local storage for the homepage, downloading only around 8KB additional from the server.
- Page load times:
- Empty cache: 2.42,2.91,1.93,1.87,1.63
- Hot cache: 1.78,1.62,1.44,1.67,3.25
- Average:
- Empty cache: 2.152
- Hot cache: 1.952
- Standard deviation:
- Empty cache: 0.51178
- Hot cache: 0.73591
Latest news article
The baseline page size was 41 HTTP requests and a combined filesize of 428.8KB
- Image: 11 items, 38.6KB
- Javascript: 15 items, 234.2KB
- CSS: 4 items, 39.9KB
- HTML: 5 items, 38KB

Hot cache loads around 410.7KB from local storage for the homepage, downloading only around 18KB additional from the server.
- Page load times:
- Empty cache: 5.38,4.44,5.03,4.43,5.36
- Hot cache: 7.61,6.48,11.37,4.66,4.33
- Average:
- Empty cache: 4.928
- Hot cache: 6.89
- Standard deviation:
- Empty cache: 0.47103
- Hot cache: 2.84092
Optimisation
Pingdom and YSlow run performance audits and suggest many things which can be tweaked to improve overall site performance.
Considerations for possible performance improvements:
- Use CDNs where possible for libraries
- Improve browser caching
- Serve static resources from a cookie-less domain
- gzip compression
- Cache-Control: public for static image files
- Parallelise downloads across hostnames
- Specify image dimensions
- Optimize the order of styles and scripts
- Reduce CSS size
- Compress images
- Reduce number of images loaded
- Combine files and minify where possible
- Turn off MODX web context sessions
What we're not going to test
Specify image dimensions
This is not possible due to the responsive nature of the Rckt site layout. Image dimensions are purposely not specified on image tags to allow them to be automatically resized with the browser window/device viewport.
Optimize the order of styles and scripts - put all CSS before any JS
All CSS files should be loaded before javascript files. In the case of using Google webfonts js, the script injects a style tag into the document after the javascript is called.
All other JS/CSS on the Rckt site is loaded in the correct order, so nothing else to do here!
Improve browser caching
Tweaked to cache js and css longer:
<ifModule mod_expires.c>
ExpiresActive On
ExpiresDefault A300
ExpiresByType image/x-icon A2592000
ExpiresByType application/x-javascript A604800
ExpiresByType text/css A604800
ExpiresByType image/gif A604800
ExpiresByType image/png A604800
ExpiresByType image/jpeg A604800
ExpiresByType text/plain A300
ExpiresByType application/x-shockwave-flash A604800
ExpiresByType video/x-flv A604800
ExpiresByType application/pdf A604800
ExpiresByType text/html A300
</ifModule>
Although this tweak won't affect the page loading speeds, it can help improve future visits because assets remain cached for longer - meaning they might not need downloading again upon next visit. This is already implemented on the Rckt site.
Cache-Control: public for static image files?
As above, this will help repeat visitors but not really change initial page load times unless many people visit the same site via the same public proxy.
max-age=290304000, public = 480 weeks
<filesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf|woff|eot|ttf|svg)$">
Header set Cache-Control "max-age=290304000, public"
</filesMatch>
Again, this is already implemented on the Rckt site.
Reduce number of images loaded
An obvious way to reduce load times on any site is to reduce the number of images used. 78% of the Rckt homepage's total filesize at present is accounted for by images.
All these images are required for the design so none can be removed. Some of these images have already been sprited too. It's possible that more could be added to the sprite too.
Five images on the news article page are from external domains, 2 from Google Analytics and 3 from Disqus (who provides our commenting features). Of the remaining six images, four are part of the template and two are related directly to the article. With a hot cache, only the 2 minuscule Google Analytics images are not loaded from cache.
Again, this doesn't allow us a great deal of movement with regards to reducing the number of images required for this page.
Another option would be to convert as many template images into SVG and load them via a single javascript file, with fallback for non-javascript users to the original image files.
Looking through the template, it's possible that five images could be added to the existing sprite or made into a new one.
I started by combining the "wibbles" into one larger sprited image as these are the two largest images which are part of the main layout, but even after Kraking it was left with a single image of more than 5KB larger.
The other three images are two repeating patterns and the Rckt logo. The first two are so tiny that they are less than a quarter of a kilobyte each and the Rckt logo is placed on screen using an <img> tag, meaning I can't CSS sprite it without changing the way that part of the page is built. Not something I'm looking at for these tests.
Compress image filesizes without losing quality
Quite often, many images aren't as fully optimised as they could be, especially in the case of PNG files.
It's possible to "crush" PNG images with absolutely no loss in quality, and sometimes shave a reasonable proportion of the filesize off in the process.
The homepage loads the following images:
- 7 PNG
- 8 JPG
- 1 GIF
The GIF image is pulled in by Google Analytics, so we weren't able to make any changes to that one.
There are many programs to optimise images with, but recently I've taken to using Kraken, a web interface which allows drag and drop and a very simple and quick way to optimise images.
I ran all 21 images in the templates folder through Kraken, and made the following savings:

Homepage
Page size: 432KB
- Page load times (2pm):
- Empty cache: 1.88,3.27,1.95,1.68,1.78
- Hot cache: 1.63,1.11,1.57,1.6,1.58
- Average:
- Empty cache: 2.112
- Hot cache: 1.498
- Standard deviation:
- Empty cache: 0.65534
- Hot cache: 0.21811
Latest article
Page size: 423.9KB total, 410.7KB from cache
- Page load times:
- Empty cache: 6.05,4.37,4.65,4.8,4.16
- Hot cache: 5.74,6.23,4.53,4.47,4.64
- Average:
- Empty cache: 4.806
- Hot cache: 5.122
- Standard deviation:
- Empty cache: 0.73813
- Hot cache: 0.80893
The image below shows that it's likely Disqus is holding up a lot of our page loading times for article pages!

Serve static resources from a cookie-less domain
Although our site doesn't require cookies to function, MODX by default starts front end sessions which set a cookie. There's a neat feature on MODX 2.2.1 upwards which allows us to disable this.
Not setting a cookie will help us with this, and MODX suggest that it's possible to improve performance of pages by up to 40x using the session disable setting.
Homepage
- Page load times:
- Empty cache: 1.67,1.73,2.3,1.4,1.92
- Hot cache: 2.24,1.88,1.55,1.76,1.69
- Average:
- Empty cache: 1.804
- Hot cache: 1.824
- Standard deviation:
- Empty cache: 0.33396
- Hot cache: 0.2614
Article
- Page load times:
- Empty cache: 7.44,7.52,4.97,5.31,5.14
- Hot cache: 7.88,5.39,5.14,5.07,4.78
- Average:
- Empty cache: 6.076
- Hot cache: 5.652
- Standard deviation:
- Empty cache: 1.28761
- Hot cache: 1.26431
As seen in the image below, when more than four or five images are loaded from the same domain, you get what's known as "blocking" where the maximum number of simultaneous HTTP requests has been reached, and then your browser has to wait for other to finish downloading before others can start.

The first block of colour on each highlighted row (the light off-grey) is the "blocking" time. Reducing the frequency of these should help speed up page loads.
- Green: Connecting
- Purple: Waiting
- Rusty red: Sending
- Grey: Receiving
- Blue: DNS lookup
To try and achieve this, we can set up a specific "assets" subdomain (e.g - http://assets.rckt.co.uk), which is pointed back to the Rckt site's assets folder (/assets/templates/rckt/) - a cheeky trick which should con the browser into allow more simultaneous HTTP requests - but at a slight cost of an additional DNS lookup, and slightly larger HTML/CSS files (due to longer filepaths!).

As you can see, even though the static resources are being handled by a "fake" subdomain which actually resides within the same site, it still helps to prevent this "blocking" problem to a certain extent. We could reduce this further by hosting content images externally. This does, however, cause an additional number of HTTP requests - because a 301 redirect is encountered by the browser for each assets on the subdomain that it tries to access.
For a first run, I only changed the paths to images referenced in the CSS.
Homepage
- Page load times:
- Empty cache: 1.62,2.16,1.57,1.93,1.64
- Hot cache: 1.95,1.48,1.92,1.48,1.74
- Average:
- Empty cache: 1.784
- Hot cache: 1.714
- Standard deviation:
- Empty cache: 0.25304
- Hot cache: 0.22821
Article
- Page load times:
- Empty cache: 7.69,4.99,6.04,4.74,5.74
- Hot cache: 8.01,2.2,4.61,5.38,4.59
- Average:
- Empty cache: 5.84
- Hot cache: 4.958
- Standard deviation:
- Empty cache: 1.16243
- Hot cache: 2.08302
After a bit more digging, and realising that the subdomain shouldn't really be doing any 301 redirects, we added an additional .htaccess file to the root directory of the assets subdomain which checked the requested URL and stopped it from being rewritten back to www.rckt.co.uk/{URL} - a task which the site's main htaccess file does in order to prevent duplicate page results in Google (ie - rckt.co.uk/pagename and www.rckt.co.uk/pagename. Technically two different addresses but they actually go to the same page on the same site!)
Homepage
- Page load times:
- Empty cache: 0.896,2.08,1.65,1.8,1.5
- Hot cache: 1.65,1.31,1.27,1.26,1.41
- Average:
- Empty cache: 1.5852
- Hot cache: 1.38
- Standard deviation:
- Empty cache: 0.44086
- Hot cache: 0.16217
Article
- Page load times:
- Empty cache: 6.25,4.11,4.18,4.14,4.08
- Hot cache: 4.38,4.08,3.57,4.37,4.25
- Average:
- Empty cache: 4.552
- Hot cache: 4.13
- Standard deviation:
- Empty cache: 0.94993
- Hot cache: 0.33563
Combine files and minify where possible
Minifying files and combining them is obviously a very good and simple way or speeding up a site - it requires less HTTP requests to load the page and the files have less whitespace in them, so are usually a lot smaller than uncompressed versions.
The big problem here is future maintenance - if the files aren't combined in a way which allows simple amends at a later date, it's not worth the minor performance increase because editing those files later will be a complete nightmare!
Fortunately, the Rckt site uses Catpressor to combine and gzip files, helping reduce loading time and HTTP requests.
Catpressor is a PHP script which allows you to specify input files and when called generated both a normal concatenated version of the files passed to it as well as a gzipped version should the requesting browser support that.
This method has two major pros: The original files are not touched so are still easily maintainable, and the output of the script is both cached and available in a compressed format. Because of this, there aren't a great deal of improvements we can make in this area - it's already pretty well organised and optimised.
The one area we can make a small improvement is the CSS files. The stylesheets for the Rckt site are written in LESS format and several LESS files are combined into one single CSS file upon saving the document.
The CSS files can be automatically minified too, which saves around 10KB on the size of the base CSS file (35.5KB vs. 26.5K before compression)
51% of css is not used by the homepage - split and only load required CSS
In practice, this is a trade-off between slightly improving the homepage loading time and having to load an extra stylesheet when you visit any other page on the site.
From my point of view, either is fine and neither has any real advantage over the other.
Google analytics suggests that around 50% of visitors to our homepage will click through to at least one other page. The bounce rate for the latest article is much higher.
This would suggest that leaving the CSS as one file is the correct thing to do.
Use Content Delivery Networks Where Possible
Content Delivery Networks are sites that host popular libraries and plugins such as jQuery.
jQuery is pulled in using Catpressor and combined with other local javascript assets at the end of the HTML document.
Modernizr must be called in the head of the document to allow it to feature detect and provide "help" to browsers which do not support HTML5 elements (Internet Explorer 6, 7 & 8, we mean you!), and thus can't be called at the end of the document like the other javascript files.
Javascript in general should be called after the HTML has loaded, which helps speed things up as the renderer doesn't have to wait for all those lovely bells and whistles to finish loading before it can stick things in the right place on screen.
There's not much that can be changed here as all the javascript files are concatenated and gzipped already. I can take jQuery out of that and load from a CDN but is there really any point? One more HTTP request, more waiting or just let it get cached on the server with Catpressor?
gzip compression
It's possible to have bits of the site automatically "zipped" up and sent down the wire to browsers that support this feature. All we have to do is add the following lines to our .htaccess file:
php_flag zlib.output_compression On
php_value zlib.output_compression_level 5
The compression level can be a number between 1 and 9, where 1 is basic, quick compression and 9 is hardcore squashing monster. The higher the number, the more effort the webserver has to put into compressing the files it's about to send.
With compression enabled, the downloaded filesizes are: 346.5KB for the homepage, and 335.7KB for the latest article
Homepage
- Page load times:
- Empty cache: 1.04,0.841,1.13,1.05,1.16
- Hot cache: 1.6,1.49,1.37,1.4,1.39
- Average:
- Empty cache: 1.0442
- Hot cache: 1.45
- Standard deviation:
- Empty cache: 0.12461
- Hot cache: 0.09566
Article
- Page load times:
- Empty cache: 4.91,5.55,4.49,4.65,4.68
- Hot cache: 6.68,4.31,4.45,5.28,4.14
- Average:
- Empty cache: 4.856
- Hot cache: 4.972
- Standard deviation:
- Empty cache: 0.41591
- Hot cache: 1.0506
Conclusion
Let's compare what we had to start with against what we've got with all these tweaks in place.
Homepage
- Average:
- Empty cache: 2.152 -> 1.0442
- Hot cache: 1.952 -> 1.45
- Standard deviation:
- Empty cache: 0.51178 -> 0.12461
- Hot cache: 0.73591 -> 0.09566
We've managed to shave off over one whole second from the cold cache page load, and almost half a second of hot cache page loads.
Article
- Average:
- Empty cache: 4.928 -> 4.856
- Hot cache: 6.89 -> 4.972
- Standard deviation:
- Empty cache: 0.47103 -> 0.41591
- Hot cache: 2.84092 -> 1.0506
The load times for cold cache are much the same, but hot cache has improved by nearly 2 seconds, although this is slightly misleading due to the 2.8+ standard deviation on the original results.
It's safe to say that across the two pages, and after many hours of tweaks and retesting the site is quicker. Statistically, the improvements sound great, but perceived speed is just as important. And, while we're happy we've streamlined things and made them technically faster, the site was pretty fast in the first place and is not noticeably quicker from a user perspective.
Future possibilities
Not using disqus on the article pages could increase page load speed as there are a lot of external resources loaded for the commenting functionality.
We can convert some of the non-photographic image elements to SVG and load them using javascript with an original image as fallback for users without javascript enabled.
Sprite more stuff, including changing the way the logo is displayed to be a CSS text-replacement image.
