Skip to content

Wrong cache-control headers for 404 pages in the app router #75096

Closed
@emilioestebanez

Description

@emilioestebanez

Link to the code that reproduces this issue

https://github.com/emilioestebanez/next-15-issues

To Reproduce

npm i
npm run build
npm run start

Go to http://localhost:3000/example-200 (which is a status 200 page) and check the Cache-Control headers --> you get s-maxage=20, stale-while-revalidate=31535980

Go to http://localhost:3000/missingpage (which is a status 404 page) and check the Cache-Control headers --> you get s-maxage=31536000,

Current vs. Expected behavior

When visiting a 404 page (e.g. http://localhost:3000/missingpage) I get Cache-Control = s-maxage=31536000,

I expect it to inherit the revalidate value from the page.tsx (20 seconds in the example), and it shouldn't have this , at the end.

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 11 Pro
  Available memory (MB): 65208
  Available CPU cores: 20
Binaries:
  Node: 22.12.0
  npm: 10.9.0
  Yarn: N/A
  pnpm: 9.15.3
Relevant Packages:
  next: 15.2.0-canary.8 // Latest available version is detected (15.2.0-canary.8).
  eslint-config-next: 15.1.0
  react: 19.0.0
  react-dom: 19.0.0
  typescript: 5.7.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Runtime

Which stage(s) are affected? (Select all that apply)

next start (local), Other (Deployed)

Additional context

This was closed in #74865 without taking into account this comment:

There is code in packages/next/src/server/base-server.ts that tries to get the original revalidate time from the page that caused the 404 (I'm not sure if this is only for the pages router):

https://github.com/vercel/next.js/blob/canary/packages/next/src/server/base-server.ts#L3248

// If we are rendering the 404 page we derive the cache-control
// revalidate period from the value that trigged the not found
// to be rendered. So if `getStaticProps` returns
// { notFound: true, revalidate 60 } the revalidate period should
// be 60 but if a static asset 404s directly it should have a revalidate
// period of 0 so that it doesn't get cached unexpectedly by a CDN
else if (is404Page) {
  const notFoundRevalidate = getRequestMeta(req, 'notFoundRevalidate')
  revalidate =
    typeof notFoundRevalidate === 'undefined' ? 0 : notFoundRevalidate
} else if (is500Page) {
  revalidate = 0
}

In any case, even if you can't get the original revalidate value from the page, we should definitely not cache 404 responses for a year!

E.g. imagine you have a CMS, or an e-Commerce:

  1. You have a product in the URL /my-product.
  2. Then, you unpublish your product from your e-Commerce, thus getting a 404 page in /my-product
  3. A few days later, you want to publish the product again
  4. At this point, you need to wait for a year for the /my-product URL to be working again!

Metadata

Metadata

Assignees

No one assigned

    Labels

    RuntimeRelated to Node.js or Edge Runtime with Next.js.locked

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions