devlog/public/posts/hard-problem/index.html

83 lines
7.1 KiB
HTML

<!DOCTYPE html>
<html lang="en-us" dir="ltr">
<head><script src="/livereload.js?mindelay=10&amp;v=2&amp;port=1313&amp;path=livereload" data-no-instant defer></script>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>
Hard Problem? Invalidating the browser cache | CODING WITH ANDRZEJ
</title>
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/syntax.css" />
<link rel="stylesheet" href="/css/defaults.css" />
<script src="/js/main.js"></script>
</head>
<body>
<header>
<a href=http://localhost:1313/><h1>CODING WITH ANDRZEJ</h1></a>
<nav>
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a aria-current="true" class="ancestor" href="/posts/">Posts</a>
</li>
<li>
<a href="/tags/">Tags</a>
</li>
</ul>
</nav>
</header>
<main>
<h1>Hard Problem? Invalidating the browser cache</h1>
<time datetime="2024-11-13T14:24:21&#43;01:00">November 13, 2024</time>
<p><strong>I had a bit of an issue with my <a href="https://demos.ajstepien.xyz">website</a> recently.</strong></p>
<p>I pushed some changes incorporating images for the first time (I know &ndash; very swish, very modern), and everything seemed to be working just fine, but when I loaded the production site&hellip; in Firefox, the images were not styled. Stranger still, they <em>were</em> styled when I loaded the same page in Chrome.</p>
<p>The experienced computer touchers amongst you will be saying &ldquo;this is obviously a cache problem&rdquo;, and you&rsquo;re right, it is obviously a cache problem. Pressing <code>CTR + SHIFT + R</code> (which forces Firefox to clear the cache and do a full reload) proved this thesis, and solved the immediate problem for me, on my machine. But what about other people&rsquo;s machines? <strong>I needed to cache-bust.</strong></p>
<p>Post-processors such as Tailwind use fancy &lsquo;fingerprinting&rsquo; techniques for this, but I want something simpler than that for this project. Something I can code myself, without losing sight of what&rsquo;s happening under the hood.</p>
<h2 id="invalidating-cached-html">Invalidating cached HTML</h2>
<p>The best way to deal with the caching problem is to tell the browser not to cache our HTML in the first place. Yes, this is kind of (100%) cheating, but c&rsquo;mon bro, it&rsquo;s just one little HTML file &mdash; browsers only cache those because most websites these days are glorified SPAs where the HTML rarely changes.</p>
<p>I can stop the HTML getting cached by by adding the following meta tag, in this case to <code>index.html</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">http-equi</span><span class="err">&#34;</span><span class="na">pragma</span><span class="err">&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;no-cache&#34;</span> <span class="p">/&gt;</span>
</span></span></code></pre></div><h2 id="invalidating-cached-css">Invalidating cached CSS</h2>
<p>That&rsquo;s all well and good, but what I really need is for the browser to recognize my CSS as a new file and load it anew from the server. I could change the file name whenever I want to bust the cache, but this would get tedious very quickly. What&rsquo;s more, as far as Git is concerned, I&rsquo;d be deleting the CSS file and writing a new one with every deployment. Surely there&rsquo;s a better way?</p>
<h3 id="using-a-query">Using a query</h3>
<p>Of course there is. Look at this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;css/defaults.css?2&#34;</span><span class="p">/&gt;</span>
</span></span></code></pre></div><p>As I&rsquo;m requesting the file via http, I can append a query. Awesome. Not awesome enough though. I&rsquo;m too lazy to do this every time I push a commit, and, being human, I&rsquo;ll probably forget at a critical moment. This can only mean one thing. It&rsquo;s time to bash (🤣) out a quick build script.</p>
<h3 id="automating-query-insertion">Automating query insertion</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="nv">COMMIT</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>git rev-parse HEAD<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">sed -i <span class="s2">&#34;s/css?=\w*/css?</span><span class="si">${</span><span class="nv">COMMIT</span><span class="si">}</span><span class="s2">/g&#34;</span> index.html
</span></span></code></pre></div><p>Let&rsquo;s talk real quick about what&rsquo;s happening here:</p>
<p><code>COMMIT=&quot;$(git rev-parse HEAD)&quot;</code> gets the commit id from Git and assigns it to the variable <code>$COMMIT</code>.</p>
<p>Then, <code>sed -i &quot;s/css?=\w*/css?${COMMIT}/g&quot; index.html</code> does a find and replace on <code>index.html</code>. The regular expression <code>css?=\w*</code> matches &lsquo;css?=&rsquo; plus any number of contiguous alphanumeric characters (everything until the next quote mark, basically) before replacing these alphanumeric characters with the commit id. The flag <code>-i</code> tells sed to edit the file in place. The <code>g</code> tells it to perform the operation on the whole file.</p>
<p>Now, whenever we push a new commit, any CSS imports in <code>index.html</code> will be changed to something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">href</span><span class="o">=</span><span class="s">&#34;css/styles.css?=ab184
</span></span></span><span class="line"><span class="cl"><span class="s">410c10c1adfb8b85b03b316f72b&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">/&gt;</span>
</span></span></code></pre></div><p>Now I just need to add the build script to my Jenkinsfile, and the problem is solved.</p>
<p>Pretty neat, huh?</p>
<p>There&rsquo;s just one thing bugging me: surely I do actually want the CSS to be cached <em>sometimes</em>. Caching exists for a reason, and I don&rsquo;t want to sacrifice performance. Maybe I can modify the build script so that it only updates the CSS imports when the CSS files have changed&hellip; Sounds like a topic for another blogpost&hellip;</p>
</main>
<footer>
<p>Copyright 2024. All rights reserved.</p>
</footer>
</body>
</html>