SEO for Next.js

Next.js notFound() Returns 200: Test Status and Noindex

A measured Next.js 15.1.4 production fixture compares early and streamed notFound responses, with downloadable source and results.

Published Sep 28, 20264 min readBy RankCrab Team

A not-found screen and an HTTP 404 are related, but streaming can separate them. If the response has already started, Next.js can show not-found handling while the network response remains 200. Inspect the response, robots directives and page behavior separately.

The Next.js not-found documentation distinguishes streamed and non-streamed responses. The notFound function reference describes the noindex behavior. Your framework version and route structure still need a test.

What we actually tested

On September 28, 2026, we built an isolated local app with Next.js 15.1.4 and React 19.0.0, then ran its production server. It reused the installed dependencies for those exact versions. This is a production-build fixture, not a test of a live customer's site, a CDN or Google's indexing system. It is not evidence for every newer Next.js release.

We made GET requests using an ordinary curl user-agent string and a simulated Googlebot string. Both produced these status/directive results:

RouteFixture behaviorObserved statusNoindex in response markup
/knownNormal dynamic page200No
/earlyCalls notFound immediately404Yes
/lateLoading boundary; waits 250 ms, then notFound200Yes
/does-not-existNo matching route404Yes

Download measured results and the complete fixture source. The raw response may include serialized not-found text even for a successful route, so searching for the phrase “not found” alone is not a reliable test. The results distinguish literal heading markup from arbitrary response text.

The delayed fixture

The essential pieces were:

// app/late/loading.jsx
export default function Loading() {
  return <p>Loading fixture resource</p>
}
// app/late/page.jsx
import { notFound } from 'next/navigation'
export const dynamic = 'force-dynamic'

export default async function Page() {
  await new Promise(resolve => setTimeout(resolve, 250))
  notFound()
}

The delay is deliberately artificial and only exists to demonstrate a response starting before the missing-resource decision. It is not a recommended application architecture.

Diagnose your route

  1. Pin the version and reproduce the affected request with a production build.
  2. Include one known resource and one missing resource. Otherwise a global error can look like correct missing-page behavior.
  3. Save the status, redirect chain, full HTML and robots directives for each request.
  4. Record any loading/Suspense boundary before the lookup that calls notFound.
  5. Inspect the rendered page as well as the original response. Test the deployed host separately because middleware, rewrites and CDN errors can change the result.

Use the raw-versus-rendered HTML diff with two captured snapshots. It compares supplied text; it does not run a renderer or establish Google's indexed view. The noindex tool can interpret a copied directive.

Choose a fix based on the intended behavior

SituationInvestigation
Missing item detected before the response startsKeep the lookup and missing-resource decision before streaming where practical
Known item unexpectedly gets noindexTrace error handling and inherited metadata; do not remove all safeguards globally
Missing item returns a normal 200 shell without appropriate handlingCall the framework's missing-resource path and retest
Local fixture differs from deploymentCompare middleware, route rewrites, host responses and exact framework version

Moving work before a loading boundary can change perceived performance. Measure the tradeoff instead of removing streaming everywhere to satisfy an audit label. Never set all unknown URLs to redirect to the homepage as a substitute for deciding whether a real replacement exists.

What the test cannot prove

Changing a request's user-agent string does not make it an authentic Googlebot request. A local 200 with noindex is not proof that Search Console will call the page a soft 404. After deployment, use URL Inspection and record Google's actual observed crawl/response separately.

For the general diagnosis, read soft 404 errors. For tags delivered after initial rendering, see App Router metadata. Keep the fixture result with the version so upgrades trigger a new test rather than an assumption.

Make your next SEO change a useful one.

The free tools are ready now. Our paid product is still in development; join the launch list for an update when it is available.