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.
Updated November 17, 2022Tested 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
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:
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.
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](/).
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
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.
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.