6 min read

Burning a Weekend 2: Making my Ghost blog faster.

TL:DR: I did a bunch of Stuff that made my Ghost site load faster and get better PageSpeed Insight scores.
Burning a Weekend 2: Making my Ghost blog faster.

In part one, I recounted how updating my Ghost theme led me to enable HTTPS-everywhere and redirecting "www" URLs to non-www. By the end of it, I had practically started optimizing the site, so today let's make this Ghost blog even faster. Before we start, let's check out my site speed scores:

What an eyesore! The results were clear, my site was sluggish. The PageInsights scores especially hurt. Let's fix it.

Things I've done

1. Enabling Cloudflare CDN

Using Cloudflare is straightforward, and certainly delivered a noticeable speed burst. I just signed up and switched my nameservers. Unfortunately, testing www.blog.maskys.com resulted in a err_ssl_version_or_cipher_mismatch error. Turns out, not so straightforward after all. Cloudflare comes with a few catch-22s:

  • You can't use a custom certificate (such as one issued by Comodo or Let's Encrypt) for encrypting data sent between site visitors and Cloudflare on the free plan. You'll have to ramp up to the Business plan ($200/month).  No thanks.
  • You'll be using a shared certificate, i.e one that's shared across Cloudflare's customers' sites. This wouldn't be much of a problem, but it also doesn't work for second or third-level subdomains (like www.blog.maskys.com). Yikes.

I discovered I had 2 options: 1) Either disable Cloudflare for the www.blog subdomain and expose my origin IP or 2) Put up with Cloudflare's rules. I was scared into doing the latter, even if this meant all the work redirecting www to non-www was for naught. Here are my configuration options for speed:

  • Set SSL option to Full (Strict).
  • Enable Always Use HTTPS
  • Enable HSTS with Status: On,  Max-Age: 6 months, Include subdomains: On and Preload: On
  • Minimum TLS Version set to 1.1
  • Enable Opportunistic Encryption
  • Enable TLS 1.3
  • Enable Auto Minify
  • Enable Brotli Compression
  • Enable Rocket Loader

Additionally, I also have caching set up, with caching level set to ignore query strings and email obfuscation turned on.

2. Compress my images + Use a theme implementing responsive images

I mentioned in part 1 that I got a huge speed boost after upgrading my Casper theme because it made use of Ghost's responsive image sizes feature. I have also started to compress all my images, usually till the size is down to less than 500 KB. I used to put the files through compressJPEG/PNG, but recently I found a new favorite: Squoosh.app - Made by Google, of course. I find that its compression algorithm simply works better, and the sizes are lilliputian as well.

UPDATE: Thanks to Nathaly for suggesting the more intuitive WebPlanet for compressing images. Check it out!

3. Enabling Gzip Compression

I added- rather, uncommented- the following inside the http block in my nginx.conf file (Mine on the DO Ghost Droplet was at /etc/nginx/nginx.conf):

        # Gzip Settings

        gzip on;

        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

4. Add the font-display attribute

Since I load custom fonts on my site, I reduced the FOIT (Flash of Invisible Text) by setting the font-display option to fallback. Note: You can't do this if you use Google Fonts; These are your options.

What this essentially means is that while the custom font is being loaded, the text will be invisible for about 100 ms, then rendered in the fallback font, then as the font loads, it's swapped. (Read this awesome article about all the options.)

As an example, here's what one of my font-face declarations look like:

/* playfair-display-regular - latin */
@font-face {
    font-family: 'Playfair Display';
    font-style: normal;
    font-weight: 400;
    font-display: fallback;
    src: url('../fonts/playfair-display-v14-latin-regular.eot'); /* IE9 Compat Modes */
    src: local('Playfair Display Regular'), local('PlayfairDisplay-Regular'),
            url('../fonts/playfair-display-v14-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
            url('../fonts/playfair-display-v14-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
            url('../fonts/playfair-display-v14-latin-regular.woff') format('woff'), /* Modern Browsers */
            url('../fonts/playfair-display-v14-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
            url('../fonts/playfair-display-v14-latin-regular.svg#PlayfairDisplay') format('svg'); /* Legacy iOS */


After making these modifications, here are my scores:

Wayy better. The site's homepage now loads within ~2 seconds on a 3G network. I haven't included the Pagespeed Insight screenshots, simply because I didn't record the old score. Right now, blog.maskys.com scores 64/100 and 99/100 for mobile and desktop respectively.

A word of caution: Don't follow the light(house)

While using Lighthouse, I noticed that it would be frequently inconsistent. For example, when testing scores on my own machine (with throttling turned off), I'd get 97/100 and 99/100, but vastly lower scores when tested online. The scores still differed by ~20 points after turning on throttling. It's also weird that the Insight scores vary from Lighthouse ones, given it also runs Lighthouse under the hood. An issue is currently open in the Github repository tracking this issue.

Things I haven't done yet (But plan to)

The present scores are reasonable, but they still can be improved. Here's the list of improvements I intend to apply.

1. Using Cloudinary CDN for my Images

Cloudinary is a media optimisation platform which automates the processing and optimization of images and video, help to deliver them efficiently to your readers. This was top in my list of optimizations to make until I read about Ghost 2.0's image optimization feature.

But there still are compelling reasons to use Cloudinary:

  • It supports serving your images in different qualities, so you can not only resize but also compress further when serving small-sized images
  • You can serve images in next-gen formats, such as WebP. This can shave a few seconds off your load times. Ghost currently supports only .GIF, .JPG, .JPEG,  .PNG and .SVG
  • Your images (realistically, about 3-5 GB of them) are stored on Cloudinary for free, so you'll have more space on your droplet/VPS or allow you to switch to cheaper plans.
  • There are tons of image manipulations available, especially for cover photos on your "All Posts" page.

2. Load (uncritical) CSS and JS asynchronously

I got to know of this technique while reading this article from 2015 by Garey Storey. Currently, the browser needs to download the CSS/JS files before rendering the page. This is why I have a huge delay before the First Meaningful Paint. I plan to use these libraries in the future.

3. Inline critical styles to prevent FOUC

Basically, extract critical CSS classes and inline them into the HTML file, so that you don't have an unstyled document while your CSS file is loading (and thus avoiding the Flash of Unstyled Content).

4. Lazy Load Images

From CSS-Tricks,

Lazy Loading is a set of techniques in web and application development that defers the loading of resources on a page to a later point in time—when those resources are actually needed instead of loading them up front. These techniques help in improving performance, better utilization of the device’s resources and reducing associated costs.

Lazy loading should make the site appear even faster, even though the page will finish loading at a much later time.

5. Get Rid of Google Ads

As it stands right now, Google AdSense barely covers the site's hosting costs. If anything, the ads are obtrusive and slow down the site. I might get rid of it, and eventually replace them with donation links down the road.

Well, I'm wrapping up this post now. If you enjoyed this post, and think your friends would too, help them find it by sharing. See you in the next one.