32.1 Hugo Aliases: Built-in Redirect Generation
Now, let’s talk about Hugo’s aliases, which is a fancy way of saying “built-in redirect generation that saves you from a world of hurt.” You’ve been there: you move a page, and suddenly a perfectly good link is now a digital ghost town, leaving your users with a 404 error and a profound sense of abandonment. We’re better than that. Hugo, being the thoughtful static site generator it is, gives us the aliases front matter key to handle this elegantly.
Here’s the basic incantation. In the front matter of your content file (say, content/posts/my-brilliant-post.md), you list the old paths you want to redirect to this new one.
---
title: "My Brilliant Post"
date: 2023-10-26
aliases: [/posts/old-url, /blog/even-older-url]
---
When you run hugo build, Hugo doesn’t just shrug and move on. It gets to work. For each alias you provide, it generates a corresponding HTML file at that exact location in your public/ directory. So for the alias /posts/old-url, you’ll find a public/posts/old-url/index.html. This is genius because it perfectly mimics how most web servers work; they look for an index.html file inside a folder. The contents of that generated file are not your full post, but a minimal, atomic-powered redirect page.
How the Magic (and the Redirect) Actually Works
Open one of those generated alias files. It’s a beautiful, minimalist piece of web technology:
<!DOCTYPE html>
<html>
<head>
<title>https://your-site.com/posts/my-brilliant-post/</title>
<link rel="canonical" href="https://your-site.com/posts/my-brilliant-post/"/>
<meta name="robots" content="noindex">
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta http-equiv="refresh" content="0; url=https://your-site.com/posts/my-brilliant-post/"/>
</head>
</html>
See that <meta http-equiv="refresh"> tag? That’s the engine. It tells the browser, “Redirect yourself to this new URL immediately (with a delay of 0 seconds).” It’s a client-side redirect. This is both its greatest strength and its most significant weakness. It just works, everywhere, no server configuration needed. But it’s not as SEO-optimal or as instantaneous as a true HTTP 301 redirect. However, for the vast majority of use cases on a static site, it’s perfectly adequate and a million times better than a 404.
The Critical Pitfall: Absolute vs. Relative Paths
This is where everyone, and I mean everyone, stubs their toe at least once. The example above uses an absolute path (https://your-site.com/...). That’s because, by default, Hugo is paranoid about getting it wrong. If your baseURL in config.toml isn’t set, or if you’re using relative URLs, it can’t generate a reliable redirect. An alias file that tries to redirect to ../my-brilliant-post/ is a one-way ticket to Broken Redirect City.
Best practice: Always use absolute paths in your aliases list. Start them with a slash. This ensures the generated redirect has a fully qualified URL to target. If you feed it a relative path, the resulting HTML will contain a relative path for the redirect, which will almost certainly break.
# 👍 Do this
aliases: [/posts/old-url]
# 😱 Avoid this like it's a meeting that could have been an email
aliases: [../old-url]
Taming the Output for Your Server
You might look at this and think, “A HTML redirect? I want a proper _redirects file for Netlify!” or “I need to configure Apache!” Hugo’s got you covered there, too. The alias system is just the default behavior. You can override the template it uses!
Create a file at layouts/alias.html in your project. This now lets you control the exact output for every alias file. Want to generate a plain text _redirects file for Netlify’s serverless redirects? You can do that.
{{/* layouts/alias.html */}}
{{/* This generates a Netlify-style _redirects entry */}}
# Generated from alias {{ .Page.RelPermalink }}
{{ range .Page.Aliases }}{{ . }} {{ $.Page.Permalink }} 301{{ "\n" }}{{ end }}
This is advanced wizardry, but it demonstrates the power: Hugo gives you a simple, effective default and the keys to the kingdom if you need to change it. The important thing is that you’re never manually managing a list of old and new URLs. That list lives right with the content it’s redirecting to, which is exactly where it belongs. It’s version-controlled, it’s obvious, and it’s maintainable.