How I Optimized Core Web Vitals to 100% on My HugoGo Website

October 31, 2025
4 min read
How I Optimized Core Web Vitals to 100% on My HugoGo Website

Optimizing a HugoGo website to achieve a perfect 100% Core Web Vitals score is completely possible without major code rewrites. Hugo’s static site generation already gives a strong base — with server response times (TTFB) as low as 50–100 ms when served via a CDN like Cloudflare. My optimization focused mainly on fonts, images, and a few key layout performance tweaks.

Below is exactly how I did it.


1. Replacing Google Fonts with Self-Hosted Fonts

Using Google Fonts directly can delay rendering and lower the First Contentful Paint (FCP) score because of external network requests. To solve this, I downloaded the fonts locally and preloaded them for faster display.

Steps I Took

  1. I used https://gwfh.mranftl.com/fonts to download only what I needed:

    • Inter Tight
    • Weights: 400, 500, 700
    • Subset: latin
  2. I placed these font files inside my Hugo assets/fonts/ directory.

  3. Then, I created a partial file at:

    layouts/partials/fonts.html
    

    Here’s the code I used:

{{/* Hugo font preload + @font-face partial */}}
{{- $font400 := resources.Get "fonts/inter-tight-400.woff2" | resources.Fingerprint -}}
{{- $font500 := resources.Get "fonts/inter-tight-500.woff2" | resources.Fingerprint -}}
{{- $font700 := resources.Get "fonts/inter-tight-700.woff2" | resources.Fingerprint -}}

<!-- Preload critical fonts -->
<link rel="preload" href="{{ $font400.RelPermalink }}" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="{{ $font500.RelPermalink }}" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="{{ $font700.RelPermalink }}" as="font" type="font/woff2" crossorigin>

<style>
@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('{{ $font400.RelPermalink }}') format('woff2');
}

@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('{{ $font500.RelPermalink }}') format('woff2');
}

@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('{{ $font700.RelPermalink }}') format('woff2');
}

body, p, h1, h2, h3, h4, h5, h6, b {
  font-family: "Inter Tight", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  font-optical-sizing: auto;
  font-style: normal;
}
</style>
  1. Finally, I loaded this partial in my site header layout using:

    {{ partialCached "fonts.html" . }}
    

Result: Fonts now load instantly from my own domain with font-display: swap. No layout shift. No external Google Fonts dependency.


2. Optimizing Thumbnails and Images

Images were the biggest factor affecting my Largest Contentful Paint (LCP). To fix this, I used Hugo’s built-in image processing to create responsive WebP versions automatically.

How I Set It Up

In my post front matter (.md files), I added a thumbnail:

thumbnail: /images/post-thumb.jpg

Then in my list template (layouts/_default/list.html), I added:

{{ with .Params.thumbnail }}
  {{ partial "responsive-thumb.html" (dict "src" . "alt" $.Title) }}
{{ end }}

Next, I created a new partial:

layouts/partials/responsive-thumb.html

Here’s the full code:

{{- $src := .src -}}
{{- $alt := .alt | default "Post Thumbnail Image" -}}
{{- $priority := .priority | default false -}}
{{- $img := resources.Get $src -}}

{{- if $img -}}
  {{- $origWidth := $img.Width -}}
  {{- $origHeight := $img.Height -}}
  {{- $padding := mul (div (float $origHeight) (float $origWidth)) 100 -}}

  {{- $small := $img.Fit "600x375 q80 webp" -}}
  {{- $medium := $img.Fit "800x500 q80 webp" -}}
  {{- $large := $img.Fit "1280x800 q80 webp" -}}

  {{- $classes := cond $priority "absolute inset-0 object-cover w-full h-full" "w-full object-cover object-center group-hover:opacity-90 transition-opacity duration-300" -}}
  <picture>
    <source media="(max-width: 599px)" srcset="{{ $small.RelPermalink }}">
    <source media="(max-width: 1023px)" srcset="{{ $medium.RelPermalink }}">
    <source media="(min-width: 1024px)" srcset="{{ $large.RelPermalink }}">
    <img
      src="{{ $large.RelPermalink }}"
      width="{{ $large.Width }}"
      height="{{ $large.Height }}"
      loading="{{ if $priority }}eager{{ else }}lazy{{ end }}"
      decoding="async"
      fetchpriority="{{ if $priority }}high{{ else }}auto{{ end }}"
      alt="{{ $alt }}"
      class="{{ $classes }}"
      style="max-width:100%; height:auto;">
  </picture>
{{ end }}

Result:

  • Images load progressively.
  • CLS (Cumulative Layout Shift) is eliminated with proper width/height.
  • Images are automatically served in optimized WebP format.

3. Other Small but Effective Tweaks

  • Used partialCached in all layout includes to reduce rebuild time and improve initial load.
  • Served the site through Cloudflare CDN, keeping global TTFB around 50–100 ms.
  • Avoided any render-blocking scripts.
  • Ensured all assets were local and fingerprinted (resources.Fingerprint) for cache efficiency.

Hugo Website Speed boost Pages speed inside

4. Final Outcome

After applying these optimizations:

MetricBeforeAfter
LCP (Largest Contentful Paint)2.4 s1.0 s
CLS (Cumulative Layout Shift)0.150.00
FID (First Input Delay)30 ms10 ms
Overall PageSpeed Score80%100%

Measured via Google PageSpeed Insights and Lighthouse.


5. Key Takeaways

  • Hugo’s static generation already gives a huge performance advantage.
  • Hosting fonts locally removes external dependencies.
  • Using Hugo image processing keeps images responsive and compressed.
  • Preloading critical assets and lazy-loading the rest is the fastest path to a perfect CWV score.

By leveraging Hugo’s built-in asset pipeline, local fonts, and responsive image generation, I was able to push my site’s Core Web Vitals score to a full 100% — all without changing any major site code.

Recent Articles

How I Fixed My CI/CD: Fast, Optimized, and Pro-Level Deploys (Hugo + Cloudflare)

January 14, 2026

We’ve all been there: you push a tiny CSS tweak, and then you sit… and wait… and watch the GitHub Actions logs spin. My Hugo site was taking nearly a minute to deploy. Not …

Deploy Hugo + Tailwind v4 to Cloudflare: Super Fast GitHub Workflow

January 13, 2026

So you want your Hugo site to fly, right? Like, blink-and-it’s-live kind of speed. I got you. We’re gonna set up a GitHub Actions workflow that takes your Hugo code (rocking Tailwind v4), builds it …

Is Tailwind CSS Dying in 2026? Why v4 Might Be the Framework's Final Masterpiece

January 13, 2026

Hope you’re doing good! Honestly, there’s some pretty wild news in the Tailwind world right now. It’s a mix of “the tech is amazing” and “the business is in trouble.” …

Why Hugo is the best Static Site Generators in 2026

January 13, 2026

Thinking about building a site in 2026? Honestly, the “shiny object syndrome” is real with all these frameworks, but Hugo is still the low-key king for anyone who wants a site that just …

Windows 11 vs. The Linux Crew: Why It’s Time to Jump Ship

January 13, 2026

If you’re tired of Windows 11 acting like that one overbearing landlord who enters your apartment without knocking, it’s time we talk about Linux. Switching OSs sounds like a massive headache, but …

Upgrading openSUSE Leap 16.0 to Kernel 6.18 (LTS) via Backports

January 12, 2026

Look, openSUSE Leap 16.0 is awesome. It’s stable, it’s enterprise-grade, and it doesn’t crash. But sometimes, “stable” feels a little too much like “driving your …