There are three workarounds: reuse 404.html in a subdirectory, create 404.md, and create custom 404.html in static directory. You don’t need these workarounds if you don’t enable defaultContentLanguageInSubdir.
Hugo generates 404.html in each subdirectory. Copy an 404.html to public directory after building. hugo && cp public/en/404.html public works for Cloudflare Pages, Netlify and Vercel.
Don’t copy generated 404.html to static directory. When 404.html changes, you need to delete 404.html, build the site and copy 404.html. It’s annoying to repeat these steps.
Create 404.html in static directory. It’s copied to public directory during building.
A minimal example:
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html><!-- static/404.html --><htmllang="en"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>404 Page Not Found - Cyrus Yip's Blog</title></head><body><h1>404 Page Not Found</h1><p><ahref="/en/">English Site</a> | <ahref="/zh-cn/">Chinese Site</a></p></body></html>
<!DOCTYPE html><!-- static/404.html --><htmllang="en"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>404 Page Not Found - Cyrus Yip's Blog</title><style>body{display:flex;align-items:center;justify-content:center;flex-direction:column;height:100vh;background-color:black;margin:0;}.message{font-size:2em;color:dimgray;}a{color:white;}</style></head><body><pclass="message">You are in darkness</p><pclass="link"><ahref="/en/">Turn on the light</a></p><pclass="message">你掉入了虚空</p><pclass="link"><ahref="/zh-cn/">离开</a></p></body></html>