Busting Your Cache with the Power of Git

I’ve been using middleman since 2012. While working on the current redesign, I once again encountered the problem that I had no way of busting the cache, especially for CSS files.

While Middleman provides handy tooling for cache busting, I have not been using it because I’ve been trying to slowly wean my layouts away from using Middleman/Sinatra-specific helpers so that if I decide to move to a different static-site generator. ¯(°_o)/¯

I decided to tackle it by hand, because why not. My first try was naive to say the least.

commit 1517eabb35fe34b6d2aac4150649798658d5c6f5
Author: Evan Travers <evantravers@gmail.com>
Date:   Wed May 20 09:17:56 2020 -0500

    Try adding a cachebreaker

diff --git a/source/layouts/layout.html.erb b/source/layouts/layout.html.erb
index ae25085..b68fe67 100644
--- a/source/layouts/layout.html.erb
+++ b/source/layouts/layout.html.erb
@@ -37,7 +37,7 @@

   <link href="https://fonts.googleapis.com/css?family=Muli:300,400,400i,700|Literata:700,300&display=swap"
         rel="stylesheet">
-  <link rel="stylesheet" href="/dist/style.css">
+  <link rel="stylesheet" href="/dist/style.css?cachebreaker=<%= Time.now.to_i %>">
 </head>
 <body>
   <nav class="main_navigation">

It first appeared to work… but do you see my mistake? When Middleman builds each file, Time.now.to_i is still ticking away. Every file will get a totally different cache-breaker! While each refresh will indeed get a fresh file, every page will re-request the css file. That’s not right at all.

I realized that I needed to only change the URL when the CSS file changes. What system do I have in place that tracks changes? Source control!

commit 7ec5bc915bc78735f43ffedd8660763b73cd1c6d
Author: Evan Travers <evantravers@gmail.com>
Date:   Wed May 20 09:28:02 2020 -0500

    More intelligent cachebreaker

    This one will only update if I update the style.scss file, and it'll be
    the same on _every_ page.

diff --git a/config.rb b/config.rb
index 72d2be8..8d0b238 100644
--- a/config.rb
+++ b/config.rb
@@ -50,6 +50,10 @@ helpers do
     'evantravers.com'
   end

+  def style_cache_id
+    `git log --pretty=format:'%H' -n 1 -- source/css/style.scss`
+  end
+
   def meta_description
     if is_blog_article?
       Sanitize.clean(current_page.summary).strip
diff --git a/source/layouts/layout.html.erb b/source/layouts/layout.html.erb
index ae25085..4904350 100644
--- a/source/layouts/layout.html.erb
+++ b/source/layouts/layout.html.erb
@@ -37,7 +37,7 @@

   <link href="https://fonts.googleapis.com/css?family=Muli:300,400,400i,700|Literata:700,300&display=swap"
         rel="stylesheet">
-  <link rel="stylesheet" href="/dist/style.css">
+  <link rel="stylesheet" href="/dist/style.css?cachebreaker=<%= style_cache_id %>">
 </head>
 <body>
   <nav class="main_navigation">

So far, this works wonderfully. If I push an update without any CSS changes, no files change, and you get the benefit of the existing cache. If I push any change to the CSS file, every single page on the site gets a new URL for the style, forcing the cache to refresh. I do a little more file-moving and computation, but the user doesn’t feel the pain.

Simple, but it works.


Changelog
  • 2020-06-18 14:26:02 -0500

    Move everything to CST

    Don't know why I didn't do that before. It caused _no_ end of
    problems.

  • 2020-05-27 20:32:37 -0500

    Title case the title

  • 2020-05-27 20:29:15 -0500

    Post: Busting cache