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

Deploy Your Hugo + Tailwind v4 Website to Cloudflare Pages Automatically with GitHub Actions

November 1, 2025

This guide shows how to automatically deploy a Hugo + Tailwind CSS site to Cloudflare Pages every time you push code to your GitHub repository. It’s written for beginners — no advanced DevOps …

Best Nodejs Package Hicons for HugoGo: Effortless SVG Icon Management

October 31, 2025

SVG icons are a staple of modern web design, but managing them in static site generators like Hugo can be tedious. If you’ve ever found yourself manually copying SVG code, worrying about layout bloat, …

Why Your First Website Should Be Built with HugoGo Instead of WordPress

October 30, 2025

When it comes to building your first website, most people immediately think of WordPress. It’s popular, widely supported, and has tons of plugins but is it really the best choice for every …

Why Hugo static website generation Can Be Better Than Next.js in 2026

October 30, 2025

When it comes to building fast, secure, and scalable websites, Hugo and Next.js both stand tall. But depending on your goals — especially if you’re running a blog, portfolio, agency site, or a …

Web Development on Android Using UserLAnd and CX File Explorer (Node.js)

October 15, 2025

If you don't have a laptop or a PC, learning web development or starting freelancing is no longer impossible! UserLAnd is an excellent solution for creating a complete development environment using an …

My Personal Blog/Portfolio Hosting Choice — VPS vs Serverless Explained

October 7, 2025

Short answer: For personal portfolio or static blog, I recommend Hugo + Cloudflare Pages. It’s fast, free, and no server headache. VPS gives more control, but needs setup and maintenance. Serverless …