How to return a 404 error in getServerSideProps with Next.js

After a lot of misunderstandings and frustration with the documentation on the Next.js docs I decided it might be worth explaining how to properly return a 404 error in getServerSideProps with Next.js.

What's the problem with Next.js and its error pages?

If you're reading this you're probably aware that Next.js has a fairly clear page about showing custom error pages.

I suppose most people will be absolutely fine with the solutions provided in the documentation. However, if you're anything like me and do a lot of SEO work, it's likely that after a couple of months you'll start noticing Google indexing pages that don't actually exist.

For the longest time I didn't understand why this was happening. I'd visit a non-existing page and Next.js would spit out a 404 error, but despite this (and a lot of wasted time on Google Search Console), those pages would be recognised as valid.

Why your Next.js error pages are not actual error pages

The solution to the problem, as it usually is more often than not, was very simple. While Next.js renders an error page, it doesn't actually respond with an error. Weird, I know.

This means that while you can be viewing a 404 error, the page is actually responding with a 200 code. To search engines, this essentially translates to: "Everything went fine and we found the page". Rather than actually responding with a 404, which tells search engines that page doesn't exist.

In retrospect this actually makes sense, as it's impossible to change the response code from a component. Unfortunately this prospect didn't even cross my mind for the longest time.

How to respond with a 404 error in Next.js

Returning a 404 error in Next.js' getServerSideProps is actually really simple. In your getServerSideProps, where you're fetching the data for your page, you'll just need to add a couple of lines of code:

export async function getServerSideProps({ res }) {
  const data = await fetch("https://api.github.com/repos/vercel/next.js");
  const errorCode = data.ok ? false : data.statusCode;
  if (errorCode) {
    res.statusCode = errorCode;
  }
  const json = await data.json();

  return {
    props: { errorCode, stars: json.stargazers_count }
  };
}

The main difference between the code snippet we have above and the one in the docs is that we manually set the response with res.statusCode = errorCode;.

Here we're simplifying the code a lot by expecting our server to also throw us the relevant error. (But if your API isn't set up to handle that you can simply set it manually like so: res.statusCode = 404.)

Then, in our component we can simply check if our errorCode prop exists and display our component before rendering anything else.

export default function Page({ errorCode, stars }) {
  if (errorCode) {
    return <Error statusCode={errorCode} />;
  }
}

That's all!

At Ironeko we're big fans of Next.js, if you're one too make sure to check out our running Next.js in Capacitor on Android and running Next.js in Capacitor on iOS guides!