React Server Components, the App Router, client-side navigation, and CDN caching
Unanswered
Spectacled bear posted this in #help-forum
Spectacled bearOP
tl;dr: With the App Router, when a client-side navigation happens, the browser makes a request to the server for the new RSC payload of the destination route. But this request/response seems to be uncacheable at the CDN layer. Is this correct?
-----
I run a high-traffic, self-hosted ecommerce site with hundreds of thousands of pages. All Pages Router at the moment. Caching at the CDN is crucial for us - hitting the actual server is always a last resort at this scale. We cache both HTML documents and JSON API responses, so we can often skip the next.js server for both hard loads and client side navigations.
In short:
- When a customer comes to us from google, they can get the full page from the CDN, skipping the next.js server
- When a customer clicks a link from one product page to another product page:
- They already have all the javascript to render the product page
- So if we fetch the product data from the browser, we can skip the next.js server
- The product API is also CDN-cached for speed
We use
With the App Router, it seems that client-side navs will always trigger a request to the server for the RSC payload of the destination page. And crucially, this request contains a query param like
I have found a couple of threads on github which discuss this but no definitive response:
- https://github.com/vercel/next.js/discussions/59167
- https://github.com/vercel/next.js/issues/65335
Am I right in thinking that the App Router is just incompatible with CDN caching during client side navs? It seems like there is no way to avoid hitting the origin server. What am I missing?
-----
I run a high-traffic, self-hosted ecommerce site with hundreds of thousands of pages. All Pages Router at the moment. Caching at the CDN is crucial for us - hitting the actual server is always a last resort at this scale. We cache both HTML documents and JSON API responses, so we can often skip the next.js server for both hard loads and client side navigations.
In short:
- When a customer comes to us from google, they can get the full page from the CDN, skipping the next.js server
- When a customer clicks a link from one product page to another product page:
- They already have all the javascript to render the product page
- So if we fetch the product data from the browser, we can skip the next.js server
- The product API is also CDN-cached for speed
We use
getInitialProps so that a single data fetching function can run either on the server or the client depending on whether it's a hard or soft nav.With the App Router, it seems that client-side navs will always trigger a request to the server for the RSC payload of the destination page. And crucially, this request contains a query param like
_rsc=[some_hash], where the hash varies depending on where you are coming from. The response also seems to vary ever so slightly in a similar way - there is some sort of ID or hash in the response which seems to change.I have found a couple of threads on github which discuss this but no definitive response:
- https://github.com/vercel/next.js/discussions/59167
- https://github.com/vercel/next.js/issues/65335
Am I right in thinking that the App Router is just incompatible with CDN caching during client side navs? It seems like there is no way to avoid hitting the origin server. What am I missing?
7 Replies
@Spectacled bear **tl;dr**: With the App Router, when a client-side navigation happens, the browser makes a request to the server for the new RSC payload of the destination route. But this request/response seems to be uncacheable at the CDN layer. Is this correct?
-----
I run a high-traffic, self-hosted ecommerce site with hundreds of thousands of pages. All Pages Router at the moment. Caching at the CDN is crucial for us - hitting the actual server is always a last resort at this scale. We cache both HTML documents and JSON API responses, so we can often skip the next.js server for both hard loads and client side navigations.
In short:
- When a customer comes to us from google, they can get the full page from the CDN, skipping the next.js server
- When a customer clicks a link from one product page to another product page:
- They already have all the javascript to render the product page
- So if we fetch the product data from the browser, we can skip the next.js server
- The product API is *also* CDN-cached for speed
We use `getInitialProps` so that a single data fetching function can run either on the server or the client depending on whether it's a hard or soft nav.
With the App Router, it seems that client-side navs will always trigger a request to the server for the RSC payload of the destination page. And crucially, this request contains a query param like `_rsc=[some_hash]`, where the hash varies depending on where you are coming *from*. The response also seems to vary ever so slightly in a similar way - there is some sort of ID or hash in the response which seems to change.
I have found a couple of threads on github which discuss this but no definitive response:
- https://github.com/vercel/next.js/discussions/59167
- https://github.com/vercel/next.js/issues/65335
Am I right in thinking that the App Router is just incompatible with CDN caching during client side navs? It seems like there is no way to avoid hitting the origin server. What am I missing?
But this request/response seems to be uncacheable at the CDN layer. Is this correct?yes and no. Yes, you can cache it, but no then your page won't load correctly on future other clients.
Nextjs loads only the data that is changed and replaces the dom. If you want to leverage other techniques like caching a whole route, you need to use the
a tag instead of the <Link> tagSpectacled bearOP
yes and no. Yes, you can cache it, but no then your page won't load correctly on future other clients.
So, "no", then? 🙂 If it won't work properly then it's uncacheable.
If I use an
a then I'll be doing a hard reload and will lose state right? I want a soft nav which preserves client state, without having to hit the next.js server. It sounds like this isn't possible, so every single navigation on my site will have to hit the next.js server, and my CDN won't save me from this. Pretty huge limitation...@Spectacled bear > yes and no. Yes, you can cache it, but no then your page won't load correctly on future other clients.
So, "no", then? 🙂 If it won't work properly then it's uncacheable.
If I use an `a` then I'll be doing a hard reload and will lose state right? I want a soft nav which preserves client state, without having to hit the next.js server. It sounds like this isn't possible, so every single navigation on my site will have to hit the next.js server, and my CDN won't save me from this. Pretty huge limitation...
If it won't work properly then it's uncacheable.technically it is possible.
It's the same for server actions. Technically they are API endpoints, but you don't define an api route
I want a soft nav which preserves client state, without having to hit the next.js server.That sounds more like you want a browser cache (persists client state and does not hit the server again when it was recently loaded)
@Spectacled bear solved?
Spectacled bearOP
No, not solved. I'm not talking about browser caching. I'm talking about CDN caching so that one customer can benefit from a different customer having already loaded a particular route. This is what I want:
1. Customer A hard-loads product X
2. Customer A clicks a link from product X to product Y
- The RSC payload for product Y is now cached in my CDN
3. Customer B hard-loads product Z
4. Customer B clicks a link from product Z to product Y
- The RSC payload for this client-side transition is served from my CDN, without hitting the next.js server
From what I can gather, this is not possible today, though it seems like it's being worked on: https://github.com/vercel/next.js/issues/65335#issuecomment-2583401824
1. Customer A hard-loads product X
2. Customer A clicks a link from product X to product Y
- The RSC payload for product Y is now cached in my CDN
3. Customer B hard-loads product Z
4. Customer B clicks a link from product Z to product Y
- The RSC payload for this client-side transition is served from my CDN, without hitting the next.js server
From what I can gather, this is not possible today, though it seems like it's being worked on: https://github.com/vercel/next.js/issues/65335#issuecomment-2583401824
@Spectacled bear No, not solved. I'm not talking about browser caching. I'm talking about CDN caching so that one customer can benefit from a *different* customer having already loaded a particular route. This is what I want:
1. Customer A hard-loads product X
2. Customer A clicks a link from product X to product Y
- The RSC payload for product Y is now cached in my CDN
3. Customer B hard-loads product Z
4. Customer B clicks a link from product Z to product Y
- The RSC payload for this client-side transition is served from my CDN, without hitting the next.js server
From what I can gather, this is not possible today, though it seems like it's being worked on: https://github.com/vercel/next.js/issues/65335#issuecomment-2583401824
yes, that's not possible today with nextjs. If you use
atags, then you can get the "full" page to cache it and serve this "full" page by your cdn. But as we talked about, you would lose the state and get the state cached by the cdn@Spectacled bear solved?