Improving Web Site Performance On Camping.Info

by Oliver 17. November 2015 23:11

We've recently been doing some optimization work on Camping.info to improve user experience through faster web site load times.

This post goes into the details of the optimization steps and their effect on the site's performance.

Measure, Improve, Measure again

To be confident that the changes we will introduce to Camping.info actually improve the performance or perceived performance of the site, we set up an automated test harness on TeamCity using the webpagetest API wrapper node module and a custom powershell wrapper script around that which collects the test results and reports them to TeamCity.

The following paragraphs will go into some detail on the concrete steps we took to improve our users's experience.

Include external script in existing bundle

As described in Avoid Blocking Requests on External Domains, we chose to include the Bugsnag javascript library in our already existing script bundle. This saves one request and one DNS lookup. Here's a look at the start page performance before and after:

image

The savings are humble but noticeable – the Time To Start Render drops from >1200 ms to 1100-1200 ms, which in practice will correlate with a slightly faster page appearance.

Host jQuery on your own server – or don't

Based on the previous improvement I assumed that saving a DNS lookup alone could already help in improving perceived performance. So for loading jQuery we switched from cdnjs.cloudflare.com to our own domain. It turns out though that this didn't have any impact on rendering or load times.

This is actually a tricky optimization – it depends a lot on who your audience is and what sites they visit. Loading e.g. jQuery from an external host will either save one request because the client's browser might have that resource cached after visiting a totally unrelated site that includes the same library, or your user will pay for an extra DNS lookup as compared to just loading the library from your own server. The decision is up to you.

Load external javascript after window.load

A large block of potential optimization on Camping.info actually concerns the loading, parsing, and execution of javascript. Due to the organic growth of the site over the last 8 years, deferring javascript execution to a point in time after the page has actually rendered turns out to be a complex issue. We still have plenty of pre-loaded or inline javascript blocks, which is mostly due to the way ASP.NET WebForms and its UpdatePanels work. The only easy solution to dependency managament for all of those code blocks was to simply load all dependencies before the HTML that refers to them. This pattern, unfortunately, has lead to one large script bundle that we load in the <head> section of the page because loading it later would mean breaking all inline script block execution. Fixing this will require significant refactoring and thorough testing.

But there still is room for improvement! All external javascript can safely be loaded after the page has rendered. This includes Facebook buttons, the AddThis widget, and advertisment scripts. We already had most of these scripts loading after the window onload event, but additionally deferring loading of connect.facebook.net/en_US/fbds.js showed the following improvement on the start page:

image

Now, while the render start time did not decrease, the page load time decreased from around 1.8s to 1.5s. Thi is definitely a decent improvement but please don't overrate it – most of the page's content had probably already been loaded even in the old version. But now we can at least be sure that all Facebook assets will definitely be loaded only after all of the page's own assets have been loaded. And that's good.

It turns out that on a different page, the improvement after this single change is actually even more significant:

image

Here we can see that the deferred loading of the Facebook script actually improves not only the page load time, but also the start render and DOM content ready times.

One script is still being loaded before the onload event – Google Analytics. I couldn't quite convince myself to defer its loading until after onload, because we use it to track some user metrics and timings, and I felt that GA might not report the same quality of results if loaded too late. Please leave your opinions on this topic in the comment section.

Specify image dimensions inline to speed up rendering

The worst grade in our PageSpeed score was actually for not specifying image dimensions, neither in HTML nor in CSS:

Screenshot-20151117-113636

So we went ahead and did that for the start page. Here's how that improved our score:

Screenshot-20151117-212440

I honestly cannot tell any difference in performance with image dimensions provided. There are several possible causes for this:

  • maybe the images in the above-the-fold content are loaded fast enough to not delay page rendering
  • maybe the page's CSS allows the browser to start rendering even without knowing the exact image dimensions
  • something that I have no clue about at the moment.

Loading CSS file from same domain

To speed up rendering it also seemed to be a good idea to deliver our site's CSS file from the same domain as the HTML, thus saving a DNS lookup during the early stage of page rendering. Actually, the start render time dropped a bit by doing that but unfortunately the page load time increased a bit indeterministically:

image

It's safe to assume that the additional load time was caused by the fact that all image resources that are referenced in our CSS were now also being retrieved from the main domain instead of the cookieless one which in turn delayed loading of other image resources. For now we reverted this change, but we know that we can further optimize the render process by serving out CSS even faster. It would probably also help a lot if we split our large CSS file into smaller ones that could be loaded per page.

Changes without performance impact

  • Wrapping inline javascript blocks in $().ready()

Todos for the next performance sprint

  • defer loading of as many javascript files as possible to after the onload event
  • combine and minify ASP.NET AJAX's ScriptResource.axd and WebResource.axd files
  • load CSS from page domain but referenced images from cookieless domain (try css-url-rewrite)
  • load less CSS per page – ideally inline the CSS needed for the above-the-fold content
  • use HTML and CSS instead of images for our Google map buttons – this will save a ton of requests on the search page

Where are we at now?

Screenshot-20151117-232241

Happy performance tuning!

Comments are closed

About Oliver

shades-of-orange.com code blog logo I build web applications using ASP.NET and have a passion for javascript. Enjoy MVC 4 and Orchard CMS, and I do TDD whenever I can. I like clean code. Love to spend time with my wife and our children. My profile on Stack Exchange, a network of free, community-driven Q&A sites

About Anton

shades-of-orange.com code blog logo I'm a software developer at teamaton. I code in C# and work with MVC, Orchard, SpecFlow, Coypu and NHibernate. I enjoy beach volleyball, board games and Coke.