Thumbnail image

Hugo beyond the tutorial: what made me drop WordPress after 20 years

Table of contents

I already published the step-by-step guide for building this blog: repository on GitHub, Terminal theme, deploy on Cloudflare, all documented. This post is about something else: I want to explain what made me leave WordPress after twenty years, what changed on the web around us, and what Hugo offers today for people who publish content.

The text draws from my personal experience, but brings concrete data and features. It works for those who know WordPress inside and out and for those choosing a platform for the first time.

Twenty years of WordPress (and why I never left)

I started with WordPress version 2.something. Back then the software was little more than a blogging system, but it had a rare quality: it worked. You installed it, picked a theme, wrote, published. The cycle was short and rewarding. For someone who spent all day in terminals and configuration files, having a visual panel where things simply happened was a relief.

WordPress kept growing and I grew with it. I learned to build themes, pick plugins, optimize the database, configure caching, deal with updates that broke everything and with those that broke nothing. When I started reselling hosting through PortoFácil, WordPress was the default answer for any client who needed a website. And it worked, most of the time, and still does, to be fair.

Technological inertia doesn’t come from laziness, but from competence: when you master a tool to the point of solving any problem with it in minutes, switching to another means accepting weeks of voluntary incompetence. I knew how to configure Nginx to serve WordPress with my eyes closed, diagnose a slow site by reading the MySQL slow query log, which caching plugin to use and which to avoid. All that accumulated knowledge worked as a wall between me and any alternative.

I tried Hugo once, years ago. I found the documentation confusing, the template system unintuitive, and the lack of an admin panel a step backwards. I tried Ghost too, which promised to be WordPress without the downsides, but brought its own, including Node.js and a monthly subscription for the hosted version. In both cases, I went back to WordPress in less than a week, with the comfortable feeling of someone who confirms they were already in the right place.

The problem is that the right place shifted without me noticing.

The dead Internet and the cost of running a dynamic server

In 2024, automated web traffic surpassed human traffic for the first time. Imperva’s Bad Bot Report recorded 51% of all traffic coming from bots. In 2025, Ahrefs analyzed 900,000 newly published pages and found AI-generated content in 74.2% of them. Reddit co-founder Alexis Ohanian publicly stated that the dead Internet theory “is real”. This is no longer speculation, and it’s not just me saying it.

The volume of synthetic content directly affects anyone running dynamic sites. Bots don’t just read pages: they test login forms, look for outdated plugins, scan directories for exposed files. A standard WordPress installation with PHP, MySQL, and half a dozen plugins offers a generous attack surface for this kind of automation. Every plugin is a possible entry point, every database call is an opportunity for SQL injection. The wp-admin endpoint open on port 443 is a permanent invitation.

Anyone who administers WordPress professionally knows the weight of this maintenance. Core, theme, and plugin updates need to happen frequently, and each one carries the risk of breaking something. Security monitoring, application firewalls, daily backups, credential rotation. Everything works, but everything costs constant time and attention.

A static site eliminates entire categories of risk: there is no database to breach or server-side code to exploit, no third-party plugins running with elevated permissions. The server delivers ready-made HTML files, and nothing else. When malicious bot traffic exceeds legitimate traffic on the open web, reducing the attack surface to zero stops being paranoia and becomes pragmatism.

The Hugo I found is not the Hugo I abandoned

When I first tried Hugo, the software generated HTML from Markdown and that was it. The template system was functional, but required patience to understand Go’s logic. The documentation had gaps. The whole experience felt like a tool built by developers for developers, without much concern for anyone coming from outside.

Hugo in 2026 is a different piece of software. The project has over 87,000 stars on GitHub and maintains a frequent release cycle, with features that would have seemed absurd five years ago.

Builds are still fast, but the scale has changed. A site with 10,000 Markdown pages compiles in under 10 seconds. The latest version supports streaming builds, which allows generating sites with over a million pages without exhausting memory. For a personal blog this is irrelevant, but for corporate documentation or news portals, it changes the game.

The asset pipeline now comes built in. Image processing (resize, crop, rotate, adjust colors, apply filters, overlay text, and extract EXIF), JavaScript bundling with tree shaking and code splitting, Sass processing, and TailwindCSS support. None of this requires external plugins or separate tools in the build pipeline. Everything lives inside the Hugo binary.

Content adapters allow pulling data from remote sources and APIs directly during the build. This means the site remains 100% static after publishing, but the source content doesn’t need to live entirely in local files.

The installation is still a single binary, compiled in Go, with no runtime dependencies. No need for Ruby, Node.js, Python, or any package manager. You download it, put it in your PATH, and everything works on any operating system.

Multilingual without plugins, without hacks

In WordPress, publishing content in more than one language requires installing a plugin like WPML or Polylang. Both work, but add complexity to the database, create conflicts with themes and other plugins, and charge annual licenses. The editing experience changes, because the dashboard needs to accommodate language selectors, linked translations, and page duplications. Every plugin update carries the risk of breaking the relationship between linguistic versions of the content.

Hugo treats multilingual as a native feature. Configuration happens in the hugo.toml file, where you define the available languages, the parameters for each one (site title, subtitle, navigation labels), and the corresponding menus. Content for each language lives in separate files within the same directory structure. A post in Portuguese goes in pt/post.md and the English version goes in en/post.md. Hugo knows the two files are versions of the same content and automatically generates the language switching links.

Interface strings like “Read more”, “Recent posts”, or “Back to top” go in translation files in the i18n directory. You write the translation once and the theme applies it according to the page’s language. No template duplication, no conditional logic scattered through the code.

The result is that adding a language to the site means creating a configuration block in hugo.toml, adding a translation file, and starting to write the translated versions of the content. No plugin to install, no license to renew, no database to keep synchronized. The post you are reading exists in Portuguese and English, generated from two Markdown files in the same repository, and Hugo handles everything else at build time.

Multilingual without plugins, without hacks

In WordPress, publishing content in more than one language requires installing a plugin like WPML or Polylang. Both work, but add complexity to the database, create conflicts with themes and other plugins, and charge annual licenses. The editing experience changes, because the dashboard needs to accommodate language selectors, linked translations, and page duplications. Every plugin update carries the risk of breaking the relationship between linguistic versions of the content.

Hugo treats multilingual as a native feature. Configuration happens in the hugo.toml file, where you define the available languages, the parameters for each one (site title, subtitle, navigation labels), and the corresponding menus. Content for each language lives in separate files within the same directory structure. A post in Portuguese goes in pt/post.md and the English version goes in en/post.md. Hugo knows the two files are versions of the same content and automatically generates the language switching links.

Interface strings like “Read more”, “Recent posts”, or “Back to top” go in translation files in the i18n directory. You write the translation once and the theme applies it according to the page’s language. No template duplication, no conditional logic scattered through the code.

The result is that adding a language to the site means creating a configuration block in hugo.toml, adding a translation file, and starting to write the translated versions of the content. No plugin to install, no license to renew, no database to keep synchronized. The post you are reading exists in Portuguese and English, generated from two Markdown files in the same repository, and Hugo handles everything else at build time.

What Hugo doesn’t do (and why that’s an advantage)

Hugo has no admin panel, no login system, no user management, no native comments, no contact forms, no shopping cart. For someone coming from WordPress, where all of this exists one plugin away, the list of absences seems long.

Each of these absences is one fewer door in the site’s attack surface. No admin panel means no /wp-admin endpoint for bots to brute-force. No database means no SQL to inject. No login system means no credentials to steal. The published site is a collection of HTML, CSS, and image files served directly by a CDN. Complexity ends at build time.

This doesn’t mean giving up dynamic features, but rather decoupling each one from the content generator. Comments come through an external service like Isso, which runs on its own server with its own SQLite database. Contact forms work via Formspree or similar services. Site search is handled by Pagefind, which generates a static index during the build and runs entirely in the visitor’s browser, with no search server.

The difference from the WordPress model is that each external service is independently replaceable. If Isso stops meeting your needs, you swap it for another comment system without touching the rest of the site. If Formspree changes its pricing, you migrate to another form service. The site itself keeps working because it doesn’t depend on any of them to exist.

In WordPress, deactivating a caching plugin breaks performance. Deactivating the SEO plugin erases metadata. Deactivating the security plugin exposes the site. Plugins form a dependency chain where each link supports the next. In Hugo, the site works with zero external dependencies. Everything you add on top is optional and disposable.

Who Hugo doesn’t serve

It would be dishonest to close this post without addressing the limitations. Hugo works very well for anyone publishing textual content, whether a blog, documentation, or institutional site. Outside that scope, the conversation changes.

Non-technical clients need a visual panel where they can edit text, swap images, and publish without opening a terminal. WordPress delivers this natively. With Hugo, the closest alternative is a headless CMS like Pages CMS or Decap CMS, which adds a graphical interface on top of the Markdown files in the repository. It works, but the editing experience will never be as polished as the WordPress editor. Anyone who isn’t comfortable with Git, Markdown, or automated deploys will need constant support.

Sites that need native e-commerce find in WooCommerce a mature ecosystem with payment gateways, inventory management, shipping calculation, and invoice generation. Replicating all of this on a static site requires integrating multiple external services (Snipcart, Stripe, logistics APIs), and the end result is more fragile and more labor-intensive to maintain than a well-configured WordPress installation.

Projects where content changes multiple times per hour, like news portals with active newsrooms, suffer from Hugo’s build and deploy cycle. Each change requires a new build and a new deploy, and even though the build takes seconds, the full pipeline (commit, push, CI build, CDN cache invalidation) adds latency. WordPress publishes the moment the author clicks “Publish”.

WordPress remains an excellent tool for the scenarios where it shines. The point of this post is not to declare a winner. It is to note that for blogs, personal sites, technical documentation, and publications where the author controls the content, the equation has changed. The cost of keeping a dynamic server exposed on the 2026 web, with its bots, its automated attacks, and its synthetic content, needs to be part of the calculation. And when it is, Hugo, with its simplicity and predictability, becomes a choice that is hard to ignore.

Related Posts