A few months ago, I switched my blog to Octopress from Wordpress. I did this for a number of reasons, one of which is I could host it entirely from Amazon Web Services.
I haven’t quite been satisfied with my Google Page Speed report until now. I’m going to walk you through my optimizations in this post:
- Reducing HTTP Requests
- Minifying/GZipping HTML/CSS/JS (with Source Maps)
- Image handling
- Uploading to Amazon S3/CloudFront with optimal caching headers and cache invalidation
Octopress is a static blog generator based on Jekyll. It has a bunch of HTML5/responsive goodies and does syntax highlighting and rescues kittens out-of-the-box.
Anyway, this post isn’t about how great Octopress is, it’s about adding stuff to your Rakefile to make it faster. You can read the docs if you want to know more.
Every HTTP request adds significant load time to your blog, so the first step to speed is to cut down the number of requests. Use of themes and widgets exacerbates the problem, but nothing we can’t handle by combining the various resources. Here are the Rake tasks I use to accomplish this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Don’t forget to combine files in order of their dependencies. Put jQuery before jQuery plugins and all that.
After combining, I have 73kb of JS and 106kb of CSS. Blogs that use any widgets will probably have a decent amount more. Using minification and GZip cuts that down to 19kb and 17kb, respectively. Similarly, we can cut the size of our HTML files very significantly by compressing them.
Unfortunately, ol’ CloudFront won’t handle GZip automatically for us, so we have to do it ourselves and add an HTTP header
Content-Encoding: gzip. Here’s some rake tasks that’ll handle that for us:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
With this, page weight for essential resources (HTML, CSS, JS and common images) is around 90kb total!
Images are becoming the biggest contributor to page weight according to the HTTP Archive.
I don’t have an automated process for optimizing images because my blog is not image-heavy, but I use ImageOptim to optimize images before using them. There is an amazing post on optimizing images on Planet Performance.
One thing I do and recommend is the use of SVG to replace icons. If you inspect the header at the top of this page, you’ll notice that the social icons are in fact inline SVG, which is supported by IE9 and all A-grade browsers. That, my friends, is progressive enhancement for the 99.5% of you who aren’t on IE8 and lower (from my blog stats). An alternative is icon fonts, but I didn’t feel like it was worth the extra request. Come at me, retina screens!
By the way, if you’re curious how those icons came into being, I simply collected the generated SVG from the Raphael icon set, but I’m sure there’s an easy way to do this…
Deploying to Servers Close to Your Reader
Now that we have tiny static files, the last piece of the puzzle is killing the latency as much as possible. I put files on Amazon CloudFront and aggressively cache assets for this reason. To make this work, we need unique file names so that we don’t have to worry about old files being cached. That’s why we version our files.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
If you look closely, I’m only updating content in the
<head>. That’s because I actually moved my
<script> tags there (gasp!) and I
async attribute so it does not block and the browser can handle evaluating it when it’s good and ready. This
CSS are being downloaded, then evaluate it later. You can look at the structure here.
Finally, we handle deployment. For syncing with Amazon S3, s3cmd is priceless. It spares me the headache that would come if I had to handle custom HTTP headers and mime-types myself. My inspiration for this setup came from this blog post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
I recommend that you download s3cmd version 1.1.0-beta2 or later as I’m using features from that version.
Lastly, we want to tell CloudFront that assets have changed by invalidating the old assets. That feature is supposed to be in s3cmd v1.1.0 but doesn’t seem to work yet, so I wrote a script for it.
Putting It Together
To put all of these new Rake tasks to use, we update our
1 2 3 4 5
And we’re done! This cut down my average post load time from 7 to 3 seconds, including ~40 requests to disqus/twitter/etc. Here’s a chart with page timings from Google Analytics:
If that sounds unimpressive, so you should know that
DOMContentLoaded typically fires in under 400ms with an empty cache
(unless you’re in Russia, apparently).