<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hugo on Nick Liu - Software Engineer</title><link>https://nick-liu.com/tags/hugo/</link><description>Recent content in Hugo on Nick Liu - Software Engineer</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>nickboy@users.noreply.github.com (Nick Liu)</managingEditor><webMaster>nickboy@users.noreply.github.com (Nick Liu)</webMaster><copyright>2026 Nick Liu</copyright><lastBuildDate>Sat, 11 Jul 2026 15:08:00 -0700</lastBuildDate><atom:link href="https://nick-liu.com/tags/hugo/index.xml" rel="self" type="application/rss+xml"/><item><title>Blowfish supports four analytics providers. Cloudflare Web Analytics isn't one.</title><link>https://nick-liu.com/posts/cloudflare-web-analytics-hugo/</link><pubDate>Fri, 10 Jul 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/cloudflare-web-analytics-hugo/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;
 
For six months I assumed nobody could tell whether anyone read this blog, because I had never added analytics. Wiring up Cloudflare Web Analytics by hand taught me two things: the obvious place to paste the snippet would have shipped my Playwright suite's page views into the dashboard, and the dashboard had been quietly counting my visitors for two months anyway.

&lt;/div&gt;

&lt;div class="tested-with" role="note"&gt;
 &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;
 &lt;span&gt;Tested with &lt;strong&gt;Hugo&lt;/strong&gt; &lt;code&gt;0.163.3&lt;/code&gt; · Blowfish 2.104&lt;/span&gt;
&lt;/div&gt;


&lt;h2 class="relative group"&gt;Publishing into the void
 &lt;div id="publishing-into-the-void" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#publishing-into-the-void" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;The site&amp;rsquo;s &lt;code&gt;hugo.toml&lt;/code&gt; had a &lt;code&gt;googleAnalytics&lt;/code&gt; line commented out since roughly the first commit. I never uncommented it. GA4 wants a cookie disclosure, ships a chunky client, and ad blockers eat it anyway, which felt like a lot of ceremony for a personal blog whose one open question was &amp;ldquo;does anybody visit.&amp;rdquo;&lt;/p&gt;</description></item><item><title>My og:image URLs were broken for months. baseURL was the culprit.</title><link>https://nick-liu.com/posts/hugo-baseurl-dual-domain-canonical/</link><pubDate>Fri, 10 Jul 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/hugo-baseurl-dual-domain-canonical/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;
 
Paste one of my post links into a social preview and the card comes up with no image. The site itself renders fine, every page, every browser. The culprit was one character in `hugo.toml`: `baseURL = "/"`, which quietly turns every absolute URL the site emits into a relative one that only a browser can love.

&lt;/div&gt;

&lt;div class="tested-with" role="note"&gt;
 &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;
 &lt;span&gt;Tested with &lt;strong&gt;Hugo&lt;/strong&gt; &lt;code&gt;0.163.3&lt;/code&gt; · Blowfish 2.104&lt;/span&gt;
&lt;/div&gt;


&lt;h2 class="relative group"&gt;The symptom
 &lt;div id="the-symptom" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-symptom" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;Share cards without images, that was the visible part. View source on any page and the metadata told the fuller story:&lt;/p&gt;</description></item><item><title>A typo fix shouldn't boot a browser in CI. Mine did for months.</title><link>https://nick-liu.com/posts/content-pr-ci-path-filter/</link><pubDate>Sun, 21 Jun 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/content-pr-ci-path-filter/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;
 
I changed one sentence in a blog post, opened a pull request, and watched CI spend about 85 seconds installing a headless Chromium to confirm my prose still turned into HTML. The obvious fix, telling the workflow to ignore content paths, would have quietly broken every merge instead.

&lt;/div&gt;


&lt;h2 class="relative group"&gt;Symptom: a browser for a one-line edit
 &lt;div id="symptom-a-browser-for-a-one-line-edit" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#symptom-a-browser-for-a-one-line-edit" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;Every pull request on this blog runs two jobs: a &lt;code&gt;lint&lt;/code&gt; job, and a
&lt;code&gt;build-and-test&lt;/code&gt; job. The second one builds the site with Hugo, link-checks the
output with htmltest, then installs a headless Chromium and runs a Playwright
suite against a live &lt;code&gt;hugo server&lt;/code&gt;. End to end, roughly 85 seconds, and almost
all of it is Playwright.&lt;/p&gt;</description></item></channel></rss>