Skip to content
WebInsights

Error pages that recover — 404, 500 and what must not die

The error page is honest when the status code is true and the user has a next step.

A 404 is a way forward, not a brand joke. A 500 is an honest pause, not a reload loop. The page you show on error is still your site.

14 min read
Stylised browser window with performance graphs — a 404 page with clear ways onward, not an empty error or a brand joke.

Web & recovery

If you are searching for a 404 page, or for a custom 404, or for website error page, you are usually here: you have a page that says 'this page does not exist', and you are unsure whether it counts. Someone has an illustration and a link to the homepage. Someone returns 200 with the text 'not found', so Google crawls a hole as if it were content. Someone has a 500 that auto-reloads in a loop while origin is down. Someone has the WordPress default, Next.js's bare `not-found`, and a CDN error that does not look like the site at all.

The monitoring post is about noticing the site is down: uptime, the form, Search Console. This one is about what the user sees when a URL is dead, when origin fails, or when an action did not go through. An honest error page is recovery: the status code is true, the next step is visible, and what can still work — search, contact, the pages you know are alive — is nearby. An illustration is allowed. It is not the job.

TL;DR

The main points

  • A 404 must be a 404. A soft 404 (200 with 'not found') is a hole you feed the index.
  • Give a next step: search, paths you know are alive, contact — not only 'go to the homepage'.
  • A 500 must not auto-reload in a loop. It should say the failure is yours, and what the user can do.
  • The error page is still your site: the same header, the same language, the same cookie-banner duty, the same accessibility.
  • Keep the URL they asked for visible. It is a trail for support and for you when you fix the redirect.
  • Test 404, 500 and a failed action — not only that the illustration's SVG renders.

Why this matters in the first place

A dead URL with no way onward costs you the session you already paid to get. The user came from an old newsletter, a PDF link, a quote in a post, a misspelling. They land on an empty 404 or a homepage you sent them to with a 302, and they leave. You think you have 'a 404 page'. You have a stop. Recovery is the boring part: show that you understood the URL, show three paths that live, show search, show how they reach you. The homepage is one link. It is not a strategy.

Teams inherit the wrong default. WordPress shows a theme 404 nobody edited. Next.js shows `not-found.tsx` with one paragraph. The CDN shows a vendor page in another language when origin is down. Three truths, none of them yours. Monitoring tells you something is wrong. The error page is what the customer sees while you are still looking at the log. The two posts are neighbours. They are not the same job.

It is also an SEO and trust question. A soft 404 — 200 OK with 'page not found' — teaches Google that your 404s are content. A 302 from every dead URL to the homepage teaches Google that the homepage is the answer to everything, and that you do not have your paths under control. The relaunch post is cutover and 301s. Here it is the URL you did not get to fix: it should die out loud, with a 404, and with a page you are not ashamed to show a crawler.

We write this because 'custom 404' has become a design purchase you make before you have a sentence about recovery. An honest setup is boring: a true status, the same chrome as the rest of the site, a next step, and a 500 that does not make the outage worse. Without that sentence you have an illustration. You do not have an error page.

What a recovering error page actually is — and what it is not

A recovering error page is three things on one screen: you say honestly what happened (does not exist, or we failed), you let the status code match, and you give a next step that is not a guess. On 404: search, a small set of paths you know are alive, the URL they asked for, a way to contact. On 500: 'this is us', a time or a reference id if you have one, a ban on resubmitting the form blindly, a link to status if you have one. That is recovery. It is not a campaign.

It is not monitoring. Monitoring is you noticing that origin is down, that the form died, that GSC saw a crawl spike. The error page is what holds the user while that happens. You can have tidy uptime alerting and a 404 that only says '404'. You can have a loving illustration and no alarm. You need both. This post is only what the screen shows.

It is also not a redirect strategy. A 301 from an old path to the new one is right when you know the target. A 302 of every unknown path to `/` is how you hide debt and destroy the signal. The relaunch post is the list you fix. The 404 is what you show when the list does not have the row. The two must not swap. A homepage is not a 404.

And it is not a brand joke as the job. A gag, a 404 game, an 'oops' illustration is allowed if the next step is still there and the page is accessible. It is not allowed as the only effort. If you spent the design budget on the animation and left search and paths out, you bought a meme. The user arrived with an intent. Give them a path that looks like it.

What typically goes wrong when someone says we have a 404 page

The first failure is the status code. The app shows the right text and the wrong header. A client-side router fallback, a `fallback: true` without a 404, a middleware that rewrites to `/`. The crawler sees 200. The user sees 'not found'. You have lied to both. Test with `curl -sI` on a path you know is dead, and on a path you just deleted. If you only look in the browser, you are testing the layout.

The second failure is the homepage as a trap. Every unknown URL 302s to `/`, 'so they do not see an error'. Then you cannot see in the log what was dead. Google learns that the homepage is the answer to old slugs. The user loses the URL they could have sent to support. A 404 with the original path in the body is more honest — and it gives you the list the redirect work later needs.

The third failure is the 500 page that makes the outage worse. Auto-reload every five seconds. A script that hits origin again. A 'try again' button that posts the payment a second time. When origin is down, the error page should be static — cached at the edge, without calling the database to show that the database is dead. Say it is you. Give a time to try again. Save the action if you can. The reload loop is how you DDoS yourselves.

The fourth failure is that the error page is not your site. Another language. No navigation. A different cookie banner, or none, while scripts still run. A layout that breaks on mobile. The cookie-banner post is the control. The accessibility post is the EAA. Here the duty is simpler: 404 and 500 must be usable by the same person who could use the homepage. If they cannot, recovery is a screenshot.

Dead page vs. recovering page — two 404s, two kinds of next step

The dead error page

A status, a gag, a link to the homepage. The session ends here.

  • 200 or 302 instead of 404 — or a vendor 500 in another language
  • Only 'go to the homepage'; the URL they asked for is gone
  • No search, no paths you know are alive, no contact
  • 500 auto-reloads or posts the action again

The recovering error page

True status. The same site. A next step that looks like the intent.

  • 404 is 404; 500 is 500; the edge can show it without hitting a dead origin
  • The requested URL is visible; search and three-to-five live paths are ready
  • Contact or status, without forcing them through a dead form endpoint
  • No reload loop; 'try again' is a choice, not a script

How to build error pages that actually save the session

Without this order you get an illustration you call done.

  1. 01

    Inventory the ways you already die — not only `/foo`

    A path that never existed. A path you deleted. A stale campaign URL. Origin down behind the CDN. An action (purchase, login, form) that failed after submit. Each death has its job. The 404 is 'does not exist'. The 500 is 'we failed'. The action failure is 'it did not go through — do not send again blindly'. If you only design one page, you are designing the wrong one for two of the three.

  2. 02

    Lock the status code — then build the chrome

    404 must go out as 404, 500 as 500. In Next.js that is `not-found` and `error` / `global-error`, not a rewrite to the homepage. In WordPress it is the template and not catching every 404 with a plugin that 302s. Only when the header is true do the illustration and the copy matter. The soft 404 is the failure GSC already has a name for.

  3. 03

    Give a next step that looks like the intent — not only the homepage

    If you can guess the section (an old `/insights/` slug, an old product), point there. Otherwise: search, a small set of paths that actually live (services, contact, latest insights), the URL they asked for as text. The homepage is allowed. It is not the only thing. A 404 that only says 'home' is how you tell the user you did not know what they wanted.

  4. 04

    Make the 500 static enough to survive origin

    When the database or the app is down, you cannot render the error page from the same place. Cache a boring, honest 500 at the edge, or keep it as static HTML. No 'fetch related articles' on that page. No auto-reload. If you have a status page, link there. If you have a reference id, show it. The monitoring post is the alarm. Here the screen must not make the alarm worse.

  5. 05

    Test the three deaths after every deploy — and read the log afterwards

    A cold 404 you know. A deleted slug. A forced 500 (a preview that throws). `curl` on the status. A screen reader on the next step. Repeat after a relaunch and after someone 'just added a rewrite'. Then look at the log: the 404s that repeat are the redirect queue, not a design problem. An error page without that loop is a poster. It never gets smarter.

“The error page is honest when the status code is true and the user has a next step.”

Next.js, WordPress and the edge — the same duty, different traps

A Next.js site typically dies in a `not-found` you never wrote, in a rewrite that swallows unknown paths, or in an `error.tsx` that calls the app again to show that the app is dead. App Router gives you the right primitives. It also gives you a chance to pretend a client-side empty state is a 404. Keep 404 on the server. Keep 500 as close to static as you can. Preview deploys are a good place to show a colleague `curl -sI` on a dead path — before it reaches prod.

A WordPress site typically dies in the theme 404 you never opened, and in plugins that 302 'for SEO'. A cache plugin can serve a 200 cache of a page you deleted. A redirection plugin can send everything to the homepage. Inventory with a cold path and a cache bypass. If you cannot point at the template and at the status code, you do not have a 404. You have a theme.

The edge — Cloudflare, Vercel, a load balancer — is the 500 you forget. When origin does not answer, the vendor shows its own page, often in English, often without your navigation. That is still your brand for five minutes. A custom error page at the edge, cached, with no origin call, is the boring work that makes monitoring visible to the customer as 'we know', not as 'the site is gone'. Monitoring without that page is a Slack message. The customer sees the vendor's CSS.

If you already have monitoring, relaunch and an honest cookie banner, and the 404 still feels like the weak link, that is because it is. The alarm without recovery is an internal mail. Cutover without a 404 is a hope that the list was complete. The banner without a 404 that also respects consent is a leak on the page you thought nobody saw. The error page is what the wrong URL sees. It has to carry the others.

What you should typically do in the situation you are in

The left is what you typically say. The right is the next step we typically recommend.

  • Situation

    We have a nice 404 illustration and only a link to the homepage

    Next step

    Add search, the requested URL and three-to-five paths that live. The homepage is not recovery

  • Situation

    Unknown paths 302 to /, 'so they do not see an error'

    Next step

    Stop it. 404 with an honest status. Redirect only when you know the target

  • Situation

    GSC calls the URL a soft 404, and the page says 'not found'

    Next step

    Fix the status code. The text is not the signal. The header is

  • Situation

    When origin is down, the CDN shows an English vendor page

    Next step

    A static, yours, 500 at the edge. Monitoring without that page is internal

  • Situation

    The 500 page reloads automatically, 'so they come back'

    Next step

    Kill the loop. You are DDoSing yourselves. Give a time and a choice

Checklist before you call the error page recovering

If you are missing several of these, the sentence is custom 404 — the page is a poster.

  • 404 sends 404, 500 sends 500 — checked with curl, not only with the eye
  • The requested URL is visible, and there is a next step beyond the homepage
  • Search or three-to-five live paths sit on the 404
  • The 500 can be shown even if origin is dead, and it does not reload by itself
  • The page is your chrome: language, navigation, accessibility, consent
  • You have repeated the test after the last rewrite and the last time you deleted a slug

Questions we get again and again

  • What should a 404 page contain if it is going to help the user onward?

    An honest message that the page does not exist, the URL they asked for, and a next step that is not only the homepage: search, a small set of paths you know are alive, a way to contact. The status code must be 404. An illustration is allowed. It does not replace recovery. If you only have time for one thing besides the text, keep the URL and give three links that actually work. That is what saves the session.

  • May we send every unknown URL to the homepage so people do not see an error?

    Not if you want an honest index and a log you can act on. A 301 is right when you know the new path. A 302 of everything to `/` hides the debt and teaches Google that the homepage is the answer to old slugs. Show 404. Fix the repeats in a redirect list. The relaunch post is the cutover. Here the duty is to let the dead URL die out loud so you can see it.

  • What is a soft 404, and why does it do damage?

    A soft 404 is a URL that says 'not found' in the content but answers 200 — or is so thin that Google treats it as non-existent anyway. You feed the index pages you yourselves call dead. You waste crawl. You cannot tell 'we have content' from 'we have a hole'. Fix the header. Give the 404 a real 404. If GSC has already labelled it, it is not a copy problem. It is a status problem.

  • What should a 500 page do differently from a 404?

    It should say the failure is yours, not the user's, and it should survive the origin that failed. No auto-reload. No 'fetch more from the app'. A choice to try again, a time, a status link if you have one. The 404 helps someone who hit a dead path. The 500 helps someone who hit a site you cannot show. Same chrome. Different job. Mix them and you get a gag on an outage.

  • Is a custom 404 the same as monitoring the site?

    No. Monitoring is you noticing that something is down, that the form died, that crawl changed. The error page is what the user sees in the meantime. We have written about monitoring elsewhere. You can have tidy alerts and a vendor 500, or a loving 404 and nobody seeing that origin is dead. This post fixes the screen. It does not replace the alarm, and it does not replace the redirects you still owe after a relaunch.

If you have an illustration and a log full of paths you 302 away

Let us separate 404, 500 and the homepage.

A true status and a next step beat a custom 404 you call done.

Why we wrote this

This is how we think — and it's what we build.

Our insights are about the work we actually do. If this hit something you're working on, there's a concrete service that lines up.