Skip to main content

Minifying HTML on my Jekyll website

I minify all the HTML on this website – removing unnecessary whitespace, tidying up attributes, optimising HTML entities, and so on. This makes each page smaller, and theoretically the website should be slightly faster.

I’m not going to pretend this step is justified by the numbers. My pages are already pretty small pre-minification, and it only reduces the average page size by about 4%. In June, minification probably saved less than MiB of bandwidth.

But I do it anyway. I minify HTML because I like tinkering with the website, and I enjoy finding ways to make it that little bit faster or more efficient. I recently changed the way I’m minifying HTML, and I thought this would be a good time to compare the three approaches I’ve used and share a few things I learned about HTML along the way.

I build this website using Jekyll, so I’ve looked for Jekyll or Ruby-based solutions.

Table of contents

Approach #1: Compress HTML in Jekyll, by Anatol Broder

This is a Jekyll layout that compresses HTML. It’s a single HTML file written in pure Liquid (the templating language used by Jekyll).

First you save the HTML file to _layouts/compress.html, then reference it in your highest-level layout. For example, in _layouts/default.html you might write:

---
layout: compress
---

<html>
{{ content }}
</html>

Because it’s a single HTML file, it’s easy to install and doesn’t require any plugins. This is useful if you’re running in an environment where plugins are restricted or disallowed (which I think includes GitHub Pages, although I’m not 100% sure).

The downside is that the single HTML file can be tricky to debug, it only minifies HTML (not CSS or JavaScript), and there’s no easy way to cache the output.

Approach #2: The htmlcompressor gem, by Paolo Chiodi

The htmlcompressor gem is a Ruby port of Google’s Java-based HtmlCompressor. The README describes it as an “alpha version”, but in my usage it was very stable and it has a simple API.

I start by changing my compress.html layout to pass the page content to a compress_html filter:

---
---

{{ content | compress_html }}

This filter is defined as a custom plugin; I save the following code in _plugins/compress_html.rb:

def run_compress_html(html)
  require 'htmlcompressor'

  options = {
    remove_intertag_spaces: true
  }
  compressor = HtmlCompressor::Compressor.new(options)
  compressor.compress(html)
end

module Jekyll
  module CompressHtmlFilter
    def compress_html(html)
      cache = Jekyll::Cache.new('CompressHtml')

      cache.getset(html) do
        run_compress_html(html)
      end
    end
  end
end

Liquid::Template.register_filter(Jekyll::CompressHtmlFilter)

I mostly stick with the default options; the only extra rule I enabled was to remove inter-tag spaces. Consider the following example:

<p>hello world</p> <p>my name is Alex</p>

By default, htmlcompressor will leave the space between the closing </p> and the opening <p> as-is. Enabling remove_intertag_spaces makes it a bit more aggressive, and it removes that space.

I’m using the Jekyll cache to save the results of the compression – most pages don’t change from build-to-build, and it’s faster to cache the results than recompress the HTML each time.

The gem seems abandoned – the last push to GitHub was in 2017.

Approach #3: The minify-html library, by Wilson Lin

This is a Rust-based HTML minifier, with bindings for a variety of languages, including Ruby, Python, and Node. It’s very fast, and even more aggressive than other minifiers.

I use it in a very similar way to htmlcompressor. I call the same compress_html filter in _layouts/compress.html, and then my run_compress_html in _plugins/compress_html.rb is a bit different:

def run_compress_html(html)
  require 'minify_html'

  options = {
    keep_html_and_head_opening_tags: true,
    keep_closing_tags: true,
    minify_css: true,
    minify_js: true
  }

  minify_html(html, options)
end

This is a much more aggressive minifier. For example, it turns out that the <html> and <head> elements are optional in an HTML5 document, so this minifier removes them if it can. I’ve disabled this behaviour, because I’m old-fashioned and I like my pages to have <html> and <head> tags.

This library also allows minifying inline CSS and JavaScript, which is a nice bonus. That has some rough edges though: there’s an open issue with JS minification, and I had to tweak several of my if-else statements to work with the minifier. Activity on the GitHub repository is sporadic, so I don’t know if that will get fixed any time soon.

Minify, but verify

After I minify HTML, but before I publish the site, I run HTML-Proofer to validate my HTML.

I’m not sure this has ever caught an issue introduced by a minifer, but it gives me peace of mind that these tools aren’t mangling my HTML. (It has caught plenty of issues caused by my mistakes!)

Comparing the three approaches

There are two key metrics for HTML minifiers:

I’m currently using minify-html. This is partly because it gets slightly smaller page sizes, and partly because it has bindings in other languages. This website is my only major project that uses Ruby, and so I’m always keen to find things I can share in my other non-Ruby projects. If minify-html works for me (and it is so far), I can imagine using it elsewhere.