Custom 404 pages in Hugo done right

Learn how to customise your 404 page in Hugo while also avoiding an edge case “soft 404” error.

Ron Erdos
Updated November 17, 2022
Tested with Hugo version 0.105.0

NB: This tutorial is written for people hosting on Netlify, but if you use another host / CDN, you might still find the general points useful.

What is a “custom 404” page?

Let’s start at the beginning. A “404” page is what the user sees when the URL they requested can’t be found. A “page not found”, in other words. On a technical level, a “404 page” has the browser status code of 404 (hence the name).

Most of the sites you use probably have a custom 404 page. You can see an example of a custom 404 page on this site (I won’t link to one because that could mess up my link graph in Google’s eyes—just visit any nonsensical URL).

If a site doesn’t have a custom 404 page, then the “page not found” experience is just an old-school white page with “Page Not Found” in large black letters, usually in the Times font. It’s not an elegant experience; nor do you get the chance to recommend to users where they should go from there.

So a custom 404 page, for the above reasons, is a good idea.

How to generate a custom 404 page with the Hugo framework

There are two ways that I know of to generate a custom 404 page in Hugo.

Option one is easier to implement, but triggers what’s called a “soft 404” error on Google.

Option two requires more one-time effort, but rewards you with zero “soft 404” errors.

I’ll cover both of these options below; first, though—what is a “soft 404” error?

What is a “soft 404” error?

A “soft 404” error is caused when a page without meaningful content (or a page that can’t be found) has a status code of 200 (“Success”) instead of 404 (“Not Found”).

An example of a page without meaningful content is an internal search results page—on say, a cars / real estate / jobs site—with zero or low single-digit results.

Google considers a “soft 404” to be an error, so it’s something worth fixing.

Option 1: Easy but error-generating option

In this option, we have a template just for the 404 page. It would be called 404.html and live in the root of your /layouts/ folder.

When your site is generated, this would create a page called 404.html in the root of your site.

Some web hosts (such as Netlify) will see this 404.html page and automatically display it, along with the requisite 404 status code, when a page can’t be found. So far, so good. Some themes will even include this for you.

However, the downside of this approach is that the 404 template (which lives at a url of the form example.com/404/) does not itself return a status code of 404—its code is 200. This triggers a “soft 404” error with Google (see box above).

And we really shouldn’t allow our 404 page to return a status code of 200. Now, if you’re thinking that Google won’t find your 404 page, trust me, it probably will, even if you don’t link to it. I’ve seen it happen many times, and each time you will trigger an error with Google. The second option for a custom 404 page in Hugo (below) avoids this problem.

Option 2: Harder but error-free option

This option requires more work initially, but avoids triggering a soft 404 error with Google.

Here’s how to set it up:

Step 1

If it exists, delete the 404.html template in your /layouts/ folder. We won’t need it, as we’ll be using a custom page instead.

Step 2

Create a new page named 404.md in the root of your /content/ folder. This will become our new 404 page.

Here’s an example of what you could put inside this page:

---
title: Whoops! Page not found
noindex: true
layout: page
---

That page can't be found.

Our latest content is [on the homepage](/).

Step 3

In this step, when the user or Googlebot requests a url that doesn’t exist, we’re going to have Netlify show the 404.md page we created in Step 2 and also send a 404 code.

If you don’t already have a Netlify config file (netlify.toml) in the root of your Hugo site, create one and include this code:

[[redirects]]
	from = "/*"
	to = "/404/"
	status = 404

Now any time a user or Googlebot requests a page that doesn’t exist, the code above will redirect them to /404/ and correctly return a 404 response status code.

Step 4

We need to add the complementary logic in our <head> section which actually generates the noindex tag when it sees noindex: true in the YAML front matter of a piece of content.

I explain how to do this for yourself here; or you can consider purchasing the MoonBooth SEO Theme for Hugo which sets up your 404 page the right way for you, amongst many other world-class SEO features.

"Thanks so much for your work ... I'm migrating my WordPress blog to Hugo and it's been really helpful." — Francisco S., engineer and blogger

"I love your content. The information that you provide have been very useful in the development of my personal website." — Mattia C., engineer and researcher

The planets in our solar system