How I Scored a 100 in Lighthouse Performance for My Jekyll Website
This post details the steps I took, and my thought process.
To measure the baseline performance, I used the Lighthouse tool available via Chrome Developer Tools. Lighthouse gives an in-depth performance analysis and highlights opportunities to improve performance.
A score of 98 was a good starting point, but Lighthouse had identified a number of opportunities for optimisation.
I looked at ways to implement its recommendations and speed up page load.
Serve images that are appropriately-sized to save cellular data and improve load time.
The main strategy for serving appropriately-sized images is called “responsive images”. With responsive images, you generate multiple versions of each image, and then specify which version to use in your HTML or CSS using media queries, viewport dimensions, and so on.
A number of Jekyll plugins add responsive images, but Jekyll Picture Tag looked the most complete. It automatically resizes images and provides a tag that can be inserted into templates or content to generate the required HTML for displaying responsive images.
Note the following:
- Jekyll Picture Tag is not a GitHub Pages supported plugin. To add the plugin to a GitHub Pages site, the site needs to be generated either locally, or using a hosted build service such as TravisCI, then pushed to GitHub Pages.
- Jekyll Picture Tag requires ImageMagick to be installed on the system building the site.
Image formats like JPEG 2000, JPEG XR, and WebP often provide better compression than PNG or JPEG, which means faster downloads and less data consumption.
Jekyll Picture Tag can also generate WebP images by configuring its
formats option, making this an easy win.
Resources are blocking the first paint of your page. Consider delivering critical JS/CSS inline and deferring all non-critical JS/styles.
async attribute, leaving just the CSS to be optimised.
The critical rendering path represents the steps that the browser takes to render a page. We want to find the minimum set of blocking CSS, or the critical CSS, that we need to make the page appear to the user.
I ran Critical against my site to extract the critical CSS. This was added inline in the site
<head> using a Jekyll
include tag. Non-critical styles were deferred using
I also added a
Remove unused rules from stylesheets to reduce unnecessary bytes consumed by network activity.
I used UnCSS to remove unused CSS rules.
Note: Since UnCSS only runs on HTML files, it must run after Jekyll has generated the site. The “UnCSSed” stylesheet can then be added to the built site.
Leverage the font-display CSS feature to ensure text is user-visible while webfonts are loading.
Zach Leatherman’s A Comprehensive Guide to Font Loading Strategies covers various options for using the
font-display CSS feature.
I chose to replace webfonts with system fonts, rendering (pun intended) this a non-issue. No webfonts = no loading.
The number one reason for using system fonts at all is performance. Fonts are typically one of the largest/heaviest resources loaded on a website. If we can use a font already available on the user’s machine, we can completely eliminate the need to fetch this resource, making load times noticeably faster.
A long cache lifetime can speed up repeat visits to your page.
When possible, cache immutable static assets for a long time, such as a year or longer. Configure your build tool to embed a hash in your static asset filenames so that each one is unique.
The Google Developers Lighthouse reference recommends a cache lifetime of a year, provided you have a way to invalidate and update cached responses.
GitHub Pages serves all assets with a cache lifetime of 10 minutes. This isn’t configurable, but websites set up behind Cloudflare have a workaround. Cloudflare allows the cache lifetime to be overridden by setting the Browser Cache Expiration to a higher value. I set mine to 1 year.
To allow me to force visitors to download a new version of cached assets (eg. for stylesheet updates), I implemented cache-busting by adding a timestamp to the query string of asset URLs.
The Critical Request Chains … show you what resources are loaded with a high priority. Consider reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load.
No further changes were required as the critical request chain showed the high priority resources were the stylesheet and webfonts. Previous changes had already deferred the stylesheet and removed webfonts.
Running Lighthouse again after making the changes resulted in a perfect score of 100.
While the exercise was successful in achieving a perfect score in Lighthouse, it highlighted some of the limitations of the Jekyll + GitHub Pages combination.
As shown, it is possible to build and host a performance optimised site with Jekyll and GitHub Pages.