next.js: next/navigation notFound() function do not apply HTTP 404 status code

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: x64
      Version: Darwin Kernel Version 22.3.0: Mon Jan 30 20:42:11 PST 2023; root:xnu-8792.81.3~2/RELEASE_X86_64
    Binaries:
      Node: 18.13.0
      npm: 8.19.3
      Yarn: N/A
      pnpm: N/A
    Relevant packages:
      next: 13.4.4
      eslint-config-next: 13.3.0
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.0.4

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true), Routing (next/router, next/navigation, next/link)

Link to the code that reproduces this issue or a replay of the bug

https://codesandbox.io/p/sandbox/cranky-pine-f9s292?file=%2Fapp%2Fpage.tsx%3A1%2C1

To Reproduce

const BlogPage = async ({ params }: { params: { categorySlug: string; blogSlug: string; locale: string } }) => {
  const blog = (
    await dataService.collection<{ blogs: Blog[] }>('Blog', params.locale, {
      filters: {
        blog_category: {
          slug: { eq: params.categorySlug },
        },
        slug: { eq: params.blogSlug },
      },
    })
  ).blogs?.[0];

  if (!blog) {
    // Here the issue
    notFound();
  }

  return <>{JSON.stringify(blog, null, 2)}</>;
}

Describe the Bug

When notFound is called, the 404 page is correctly displayed, but the status is 200 :

image

Expected Behavior

The HTTP status code should be 404

Which browser are you using? (if relevant)

Chrome Version 114.0.5735.106 (Build officiel) (x86_64)

How are you deploying your application? (if relevant)

next start

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 27
  • Comments: 24 (1 by maintainers)

Commits related to this issue

Most upvoted comments

@ijjk Sorry for involving you, but will this ever be fixed? This is a terrible, reproducible bug, that’s been open for half a year.

Wrong status-codes served by a webserver should be a core concern.

The only way to workaround it is to not use dynamic routes. At that point I can go back to nginx/apache2.

@Enngage The issue is with the compbination of caching and dynamic routes.

If you route uses notFound() AND contains an id like [id] it will serve 404 on the first load, but 200 on the subsequent cached loads.

I suspect this is why it wasn’t fixed yet. It is too complex to reproduce.

Hey, it doesn’t return 404 on first load… that’s the problem because then google indexes the page. It’s true we are using dynamic routes with url parameters.

We actually “solved” it by creating a “/404” route and redirected notFound items to this route manually. The /404 route seems special as it applies the 404 status code, but I haven’t found it in docs. It was likely there in the old pages router, but not with the new one. It’s all bit odd.

@fab1an @robinComa

Two things were causing this behavior on my project:

  • I was using an instant loading state (loading.tsx file on the same level as the page.tsx). I don’t know the specifics, but it does make sense because the loading appears before anything else, so the server needs to send some status code.

  • I was implementing a client component that functioned as a wrapper on the whole layout, so any rendered page used it. It was a provider for i18n values, I didn’t go deeper into how it interacted with everything else to cause the wrong status code to be sent, but just removing it works.

Anyway, hope this is relevant to someone.

Same here… Are we seriously not able to set 404 status codes? The docs for notFound says:

/**
 * When used in a React server component, this will set the status code to 404.
 * When used in a custom app route it will just send a 404 status.
 */
export declare function notFound(): never;

But it clearly doesn’t work.