One of the objectives for developers of the new SitePoint website was to make it fast. Really fast.
Before we dive into what we did, I want to show you where we’re coming from:
- Time to first byte: ~3 seconds
- Document complete: 6-9 seconds
- HTTP Requests: 100+ (first time), 35+ (repeat)
As you can see, this is pretty bad. As a developer, it just hurts to see those numbers. We want to create world-class code and 100+ requests for one page just isn’t.
But, is speed just something we developers care about or does it actually matter for your business?
If you’re still not convinced, a faster backend lets you serve more traffic with the same infrastructure, saving you money. Finally, an increase in load time as been proven to lead to an increase in pageviews per visit.
So, what exactly factors into a speed-up in load time? When we think about how fast a website is, we’re most likely talking about various aspects of speed. I think it’s good to ask two questions and answer them separately even if they correlate somewhat:
- How many requests can we handle (how fast is the backend)?
- How fast does the website feel (how fast is it perceived by the user)?
We’ll look at each of these questions, and then see how we answer them here at SitePoint.
How many requests can we handle?
This greatly depends on how long one request takes when it comes in, and how many of the incoming requests we have to actually process instead of serving cached versions. At SitePoint, we’re in the lucky position to serve relatively static content, which is the same for all our visitors. This allows us to cache most of the responses, meaning most requests never hit our app.
Instead, our proxy serves in front of the website serve the response. If you check your network traffic in the browser, you’ll see most pages are served with a
304 Not modified header.
Speeding up the time it takes to generate a response is trickier, as we can’t change the internals of WordPress. However, using plugins such as W3 Total Cache, we can cache some of the more expensive parts, for example making use of the WordPress Object Cache.
How fast does the website feel?
Besides the time needed for the response to come in, we also need to address the following:
- How long does it take until the browser starts rendering?
- How long does it take until the site is ready to be used?
- How long does it take until the browser finished loading?
Technically speaking, we want to keep the time until the
DOMContentLoaded event is fired at a minimum. Quite often, a website will feel much faster without having to change any backend code when you just reduce the time to
Before telling you how we do this here at SitePoint, I want to recap some general rules when it comes to frontend performance:
- Make as few HTTP requests as possible (combine your assets)
- Download as little as you have to (minify everything and serve it gzipped)
- Cache everything as aggressively as possible
To achieve this, we generated three files during our build process:
head(which is barely anything)
All of those files are minified. Now, to be able to cache them as long as possible without having to worry about invalidation, we include the md5 hash of the content into the filename, like so:
styles-3ed1dd875e184c186ecdce24235a714d.css. With that in place, we cache each particular file for a very long time using the
Expires header (
Etag would work equally well).
If you’re using frameworks like Rails or Symfony2 to build your website, you get all of this for free. SitePoint however is running WordPress, which unfortunately does not have any support for this out of the box. There are a couple of plugins, but in the end, we decided to not use any of them.
Instead, we process our assets during build using Grunt. The reason for this is that it’s impossible to have reliable asset processing from within WordPress on a multi-server setup without a shared storage, which we don’t use anymore.
With all that in place, we’ve got quite a responsive website, serving a huge amount of traffic quite easily. Let’s see what this looks like in numbers:
- Time to first byte: ~1 second
- Document complete: 1-2 seconds
- HTTP Requests: ~20 (first time), ~7 (repeat)
For the homepage, a typical page load will fire the
DOMContentLoaded event under two seconds, with it being around one second for cached responses. Of course, these times will vary depending on your location and whether the internal cache of the page you access has been primed.
Overall, coming from 6-9 seconds and 100 requests, this is an amazing achievement which we hope you enjoy every time you point your browser to www.sitepoint.com.
Michael Sauter is a German web developer at SitePoint. He's maintaining the backend of the various SitePoint sites and works on new features and products. Usually working with Ruby or PHP - currently interested in Go and Docker.