I Built a Blazing Fast Website for $0/Month — Here's the Exact Stack (Hugo + Cloudflare Pages)

Published: (May 2, 2026 at 08:32 PM EDT)
5 min read
Source: Dev.to

Source: Dev.to

You know the feeling. You install a caching plugin to fix your slow WordPress site. The caching plugin needs a configuration plugin. The configuration plugin conflicts with your SEO plugin. Now you have three plugins doing one job badly and your Core Web Vitals score looks like a participation trophy.

I got fed up and rebuilt everything from scratch. The result is laserengraverexpert.com — a laser‑engraver review site running on Hugo 0.147.0, hosted on Cloudflare Pages, deploying in ~25 seconds, and costing me exactly $0 / month to host.

This article is the full technical breakdown: stack, config, reasoning, trade‑offs, and the one thing I got wrong at first that added two days of debugging. If you’re building a content site, affiliate site, documentation, or anything that doesn’t need a database — this stack deserves serious consideration.

The Full Stack at a Glance

LayerTool / Cost
Static Site GeneratorHugo 0.147.0 — Free
Hosting + CDNCloudflare Pages (free tier) — $0
AnalyticsCloudflare Web Analytics — $0
Build Configwrangler.toml
ImagesWebP (converted from source) — Free
DomainNamecheap — ≈ $10 / year
DatabaseNone
Server runtimeNone

Total infrastructure cost: ≈ $10 / year (the domain). Everything else is free.

Why Not the Usual Suspects?

Next.js

The ecosystem is excellent and I know React.
But Next.js is built for apps. Static export works, yet you end up fighting the framework whenever you want pure static output. The build output is heavier than needed for a content site, and Vercel’s free tier has bandwidth caps that start to matter with traffic.

Gatsby

It was the right answer in 2019.
The GraphQL data layer is powerful, but it’s overhead I don’t need. Build times become notoriously slow on larger sites, and the ecosystem has fragmented. I didn’t want to bet a production site on it.

Eleventy (11ty)

The closest competitor.
Eleventy is genuinely excellent: JavaScript all the way down, zero opinions, highly composable. If you’re already deep in the Node ecosystem and want maximum flexibility, Eleventy is your pick. I came close to using it.

Why Hugo Won

Speed – not website speed, but build speed.
Hugo builds are measured in milliseconds. My current site, with all its content, builds in under 400 ms. With Eleventy I was looking at 3–5 seconds for a comparable site. When you’re iterating on layouts and templates, that difference matters a lot during development.

  • Hugo is a single binary – no node_modules, no package.json, no dependency hell.
  • On Cloudflare Pages this means the build environment is clean, reproducible, and fast.

The Trade‑off: Go Templating

Hugo’s template syntax has sharp edges — the pipe syntax, .context scoping, and partial rendering model trip people up. I’m not going to pretend the learning curve doesn’t exist. It does. But it pays off.

Cloudflare Pages Setup

  1. Sign up for a Cloudflare account (if you don’t have one).

  2. Connect your GitHub repo. That’s the core of it — but a few specifics are worth knowing.

    • Free tier gives you 500 builds per month. For a content site with a reasonable publishing cadence you’ll never hit that. I’ve never exceeded 30 in a month.
    • Every push to main triggers a build and deploy. Every pull request gets a preview deployment with its own URL — great for reviewing changes before merging.

wrangler.toml

[build]
  command = "hugo --minify"
  publish = "public"

[build.environment]
  HUGO_VERSION = "0.147.0"

[[headers]]
  for = "/*"
  [headers.values]
    X-Content-Type-Options = "nosniff"
    X-Frame-Options = "DENY"
    Referrer-Policy = "strict-origin-when-cross-origin"
    Permissions-Policy = "camera=(), microphone=(), geolocation=()"
    Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"
    Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' cloudflareinsights.com; frame-ancestors 'none';"

[[headers]]
  for = "/css/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
  for = "/js/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
  for = "/img/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

Cache Strategy — Why Immutable Caching Is a Game Changer

max-age=31536000 tells browsers to cache files for one year. immutable tells the browser not to even bother revalidating — if the file is in cache, use it without a network round‑trip.

What if you update your CSS?
You use content‑addressed filenames. Hugo’s asset pipeline fingerprints files — it adds a hash of the contents to the filename:

{{ $css := resources.Get "css/main.css" | minify | fingerprint }}

When the file changes, the hash changes, producing a new filename, and browsers fetch the fresh asset.

The WebP Image Pipeline

WebP files are 60–80 % smaller than JPEG at equivalent visual quality.
A 400 KB product JPEG becomes a 90 KB WebP. Multiply across 30 images and you’ve saved several megabytes per visit.

{{ $original := resources.Get "images/product.jpg" }}
{{ $webp := $original.Resize "800x" | images.Convert "webp" }}
[Image: Product]

The Build Pipeline

  1. Push to GitHub.
  2. Cloudflare Pages runs hugo --minify.
  3. Hugo generates the static site in ~400 ms.
  4. The build finishes in ≈ 25 seconds end‑to‑end (including network latency).
  5. An index.json is generated at the root — ready for client‑side search (Fuse.js, Pagefind) without any future build changes.

Performance — What to Expect

MetricExpected Value
Lighthouse Performance95–100
Time‑to‑First‑Byte (TTFB)

Including the laser engraver comparison guides. Run it through PageSpeed Insights if you want to see what this stack produces in practice.

Marcus Webb writes about laser engravers and the tools he uses to build around them at .

0 views
Back to Blog

Related posts

Read more »

Your Data Lake is a Write-Only Memory

It's 2 AM when the alert hits: “Machine 47 running 15° above spec.” The factory manager asks the obvious question: > “Which other machines from this vendor are...