When I started writing this blog, I figured I could write my posts, submit my website to Google Search Console for indexing, and presto - my posts would start appearing in Google search results.
I guess I sort of forgot what it took to start a blog from zero, because I recently decided to check on my website's stats in Search Console, and I was surprised to see that Google has not been indexing my pages at all - most are marked as "Crawled - currently not indexed"!
When I looked into why the heck that would be, I discovered that trying to determine the reason for a particular page not being indexed is pretty opaque. However, the general causes seem to be:
Poor content
Not enough backlinks
Poor website performance
Well, if my content stinks there's not much I can do about that (except try harder I guess). And it's true I haven't submitted anything I've written yet to other sites like Reddit, Hacker News, etc. where people might share it and generate backlinks.
Wow! A big red 43 out of 100 - an abysmal failure. I was surprised at how bad the score was. I thought I kept my site pretty lean!
Surprisingly, some of the biggest dings I got on the report were for naively using Google products!
Google Fonts
First, loading a font using Google Fonts' "get embed code" button that uses their font CDN causes several issues:
I fixed that by self-hosting the fonts. That involved manually downloading the .css and .woff2 files that Google Fonts was fetching, and embedding the .css directly into my site's CSS and changing it to reference my self-hosted .woff2 files. I also trimmed out all the non-Latin @font-faces which weren't being used.
There's a tool called google-webfonts-helper that I found after the fact which appears to do the same thing that I did manually that I could have used.
YouTube embed
Second, naively embedding YouTube videos using YouTube's own "share" button embed code is apparently an awful idea:
Google has a performance audit page that specifically recommends using third-party libraries rather than their own embed code (!) to fix this issue.
So I fixed this issue by using justinribeiro/lite-youtube from Google's list of recommended libraries, although I made a PR because it doesn't automatically load thumbnails for playlists like how YouTube's default embed does.
Image delivery
Of the remaining issues, most of them were related to poorly optimized images.
I had only been using single, high-resolution images that I referenced directly with <img> tags. No different images based on screen size, and no lazy loading. Yeah, that was kinda dumb.
An ideal solution would be to offer multiple image formats, including modern ones like AVIF and WebP, make them responsive by offering multiple sizes for different screen sizes (by using srcset), and lazy-loading them.
On an old Jekyll blog, I had used jekyll-assets to accomplish something like that, but I remember it being a pain to get started with. And at this point, the library seems to have been abandoned as it hasn't been updated in over 5 years.
More importantly, it uses Sprockets to implement an outdated, heavyweight Rails-style asset pipeline in Ruby. That old asset pipeline does not easily integrate with modern JavaScript build tools most codebases today use like esbuild, bun, or Tailwind's CLI build tool.
Nowadays, Rails has made the wise decision to embrace JavaScript/Node tools for processing assets, and dramatically shrink Ruby's role. Today, the lightweight Propshaft library is used instead, which basically just takes post-processed CSS and JS, stamps them with a hash digest (for cache friendliness), and rewrites CSS url() references to the new asset paths as needed. Modern JavaScript build tools are easily integrated into Rails apps with this approach.
I like the Propshaft approach quite a bit, but I don't see any comparable approaches in Jekyll-land. So, I wrote one myself.
For image transformations, there's a {% image_transform %} Liquid tag:
<!-- Resize image -->
{% image_transform assets/hero.jpg[width="400"] %}
<!-- Convert format -->
{% image_transform assets/hero.jpg[format="webp"] %}
<!-- Multiple transformations -->
{% image_transform assets/hero.jpg[width="800"][format="avif"] %}
<!-- Use in img tag -->
<img src="{% image_transform assets/hero.jpg[width="400"][format="webp"] %}"
alt="Hero image" loading="lazy">
For responsive images, there's a {% srcset %} Liquid tag:
<!-- Generate multiple sizes in original format -->
{% srcset assets/hero.jpg 400 800 1200 %}
<!-- Generate multiple sizes in specific format -->
{% srcset assets/hero.jpg[format="webp"] 400 800 1200 %}
My blog setup
The way I do things in my blog is, I have a src/ directory where I have my unprocessed JavaScript and CSS stored in. I then have npm tasks like this defined in my package.json to process them and output them in the assets dir:
Amusingly, some of the only remaining issues are from external Google assets that I have no control over, like cache headers and unoptimized image formats on YouTube thumbnails:
Now all I need to do is focus on creating content and sharing it on social media…