Next.js Discord

Discord Forum

How do you set custom cache control headers in the response for pages in app router?

Unanswered
Rhinelander posted this in #help-forum
Open in Discord
Avatar
RhinelanderOP
Eg.
// Page A - cache only on CDN
"Cache-Control": "public, max-age=0, s-maxage=60, must-revalidate"

// Page B - don't cache anywhere
"Cache-Control": "no-cache, no-store, max-age=0, must-revalidate"

// Page C - cache on client and CDN
"Cache-Control": "public, max-age=60, s-maxage=60, must-revalidate"


Looks like you can't set them in next.config.js for pages: https://nextjs.org/docs/app/api-reference/next-config-js/headers#cache-control

You can't set them in the pages either in the response. How do you achieve this? I cannot believe we cannot set custom cache control for pages in app router. This is possible in pages router. I don't deploy to Vercel. I deploy to custom infra on AWS. I want to be able to set the http standard cache control headers on my page responses.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

16 Replies

Avatar
B33fb0n3
Avatar
RhinelanderOP
That doesn't let you set fine grain cache control headers like the ones I shared in my post above
Avatar
B33fb0n3
you can't directly overwrite or fine grain the cache control header. However you can use [route segment options](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config) to say how nextjs should handle the caching and revalidations
Avatar
Luke
I believe this can be controlled here:

If you're after controlling it on the page, consider separating out your data into something you can fetch from a route handler, where you can then control the caching more granularly.

Combined with react suspense, you can create an incredibly professional looking user experience

https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#opting-out-of-data-caching
Avatar
RhinelanderOP
I don't want to cache the data. I've forced the fetch handlers to use no-store. I want to be able to set custom cache control headers in the page response so the pages I specify s-maxage for gets cached on the CDN in front of my server instance for the time I specify.
Avatar
Luke
I don't think either of you read my reply correctly :/
Avatar
Luke
So either you DO want route segment options as @B33fb0n3 suggested, or you want to follow the new model for granular caching, where your page relies on uncached fetches.

The new model in the app directory favors granular caching control at the fetch request level over the binary all-or-nothing model of getServerSideProps and getStaticProps at the page-level in the pages directory. The dynamic option is a way to opt back in to the previous model as a convenience and provides a simpler migration path.

'force-dynamic': Force [dynamic rendering](https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering), which will result in routes being rendered for each user at request time. This option is equivalent to getServerSideProps() in the pages directory.

So basically, the new way of doing it is to use a fetch for your data, and never cache for your fetch.
The problem will only really come if you use something like fastly or cloudfront, which are harder to control without headers
Avatar
RhinelanderOP
I'm familiar with how the app router works. I'm not interested in caching the data. I want to cache the entire page. I want to render the entire page request time and return cache-control headers in the response so the page gets cached at the CDN in front of the server. The CDN will return from the cache and avoid origin requests as long as the cache is fresh. This was possible with pages router. I can't seem to find a way to do this with app router.
force-dynamic doesn't let you specify cache control headers. if you want to cache the page in the private cache (browser) or the public cache (CDN), you need to set the cache-control header. If you want to cache in the browser for 1 minute and in the CDN for 1 day, you will need to use the cache control header.
Avatar
Luke
☝🏻 with more context, you can see I agree
I do believe that Cache-control are written by the route segment config
Cache-control: s-maxage=60, stale-while-revalidate
will look like this:
export const dynamic = 'force-static';
export const revalidate = 60;
but it doesn't appear to go far enough to support exactly what you want
only thing I can think of, is to use middleware for those routes. i am just looking into multiple middleware for my own purposes

export function middleware(request: NextRequest) {
  const response = NextResponse.next();
  response.headers.set('Cache-Control', 'public, max-age=0, s-maxage=60, must-revalidate');
  return response;
}

export const config = {
  matcher: ['/route-a'],
};
Avatar
RhinelanderOP
I will need to try the middleware approach. If the framework overrides the cache control set in next.config.js it probably will override the cache control set in the middleware as well (i'm just guessing here). But if the cache-control headers set in the middleware are retained, perhaps path matching the request and setting custom cache control headers based on the path might be the way to go.