From Grav to Hugo

Rebuilding this blog as a static site on Cloudflare


For years this blog ran on Grav , a lovely flat-file CMS. No database, just Markdown files, which I always liked. But it still needs PHP hosting, updates, and the occasional plugin fix, and I found myself spending more time maintaining the site than writing on it. So I rebuilt it as a static site with Hugo . Here is the why and the how.

Why move at all

  • Speed. Static HTML served from a CDN is about as fast as the web gets.
  • Cost. Static hosting is effectively free.
  • Security. No PHP, no database, nothing to patch or get hacked.
  • Simplicity. Deploying is a git push, nothing else.
  • Portability. It is just Markdown and a handful of templates. Easy to move again if I ever want to.

Why Hugo specifically? It is a single Go binary, the builds are ridiculously fast, and multilingual is first-class, which matters because this blog is in English and Spanish. It also has render hooks and an asset pipeline, which made the migration much cleaner.

Moving the content

Grav stores each page as a Markdown file with YAML front matter under user/pages. Hugo uses content/ with page bundles , so the mapping was mostly mechanical:

  • Each Grav page became a bundle (index.en.md / index.es.md), which is also how Hugo links translations together.
  • Grav’s published: false became Hugo’s draft: true.
  • Grav’s taxonomy: category / tag became categories and tags.
  • Page media moved into the bundle and is referenced from the images front matter.
Grav’s notice/callout hints and its image attributes did not map one-to-one, so I recreated them as Hugo shortcodes and a Markdown render hook. That render hook also resolves relative image paths to the bundle so the files actually get published.

Search without a backend

Grav did search server-side with a plugin. A static site has no backend, so I switched to Pagefind : it builds a search index at deploy time and runs entirely in the browser. It powers the inline search right here on the blog, with zero server involved.

Hosting on Cloudflare

The site moved from a PHP host to Cloudflare as a static-assets Worker. The whole deploy pipeline is now a git push: a build runs Hugo and Pagefind, then uploads the public/ folder to the edge.

  • _redirects handles the www → apex redirect.
  • _headers sets long cache lifetimes for fonts, images and hashed assets.
  • The custom domain and SSL are automatic.

A few gotchas worth knowing

Images: Hugo does not publish a bundle’s images unless something references them. A render hook that resolves each Markdown image to its page resource fixed both the URL and the publishing in one go.
  • Translations link automatically when two files share a bundle (index.en.md / index.es.md), which also feeds the hreflang tags.
  • The 404 page: with defaultContentLanguageInSubdir Hugo only emits /en/404.html and /es/404.html, so the build copies one to the site root for the host to serve on unknown URLs.
  • Code highlighting uses Chroma with class-based styles, which let me make it theme-aware for dark mode.

A cleaner look, and a voice

While I was at it, I took the opportunity to polish the design into something cleaner and more minimalist, and to lean into a proper light/dark theme.

I also added a little Listen to post button (bottom-right on any article). It reads the post aloud using your browser’s built-in speech, and it is smart enough to skip code blocks, tables and images, swapping them for a quick “there’s a block of code here, take a look at the written version” so you are not subjected to someone spelling out XML. I will be honest: the voice is robotic and a bit awful. But if you struggle to read, or you are just feeling lazy today, it should come in handy. :)

Was it worth it

Absolutely. The site is faster (near-perfect Lighthouse scores), hosting is free, there is nothing to patch, and publishing is just a commit.

If you are running a small PHP-based site mostly to serve content, a static generator like Hugo is well worth a look.

Hope this helps.

If you have any question or made a similar move, feel free to share in the comments below.