Next.js Discord

Discord Forum

Prefetching to redirected urls is causing error in the CDN

Unanswered
Great Horned Owl posted this in #help-forum
Open in Discord
Great Horned OwlOP
I realize that when there is a prefetching to a url that contains a redirect, i.e:

/blog/?_rsc=1231 redirects to /my-new-blog/

the query param is not kept, and my CDN is caching /my-new-blog/ as a RSC instead of an HTML.

Is it possible to keep the query param in the redirect?

24 Replies

Hey @Great Horned Owl - how are you setting up the redirects at the moment? In the route handler? In next.config.js?
If you're doing it in the next.config.js, are you specifying the query param in has and redirecting to /my-new-blog/?_rsc=:rsc for example? If you're happy to share your config, that might help 😁
Great Horned OwlOP
Hi Ben! thanks for your response.

I have a JSON file with redirects:

[
  {
    "source": "/blog",
    "destination": "/my-new-blog"
  }
]


and this in next.config.ts:

  async redirects() {
    return redirects.map((x) => ({
          ...x,
          permanent: true,
        })
      );
  }


you mean to use this?


  async redirects() {
    return redirects.map((x) => ({
          ...x,
          has: [
            type: 'query',
            key: '_rsc',
            value: ':rsc'
          ],
          destination: `${x.destination}?_rsc=:rsc
          permanent: true,
        })
      );
  }


is this going to remove the query param if there is not?
I think you need to leave out the value attribute. But yes - give that a try.

I'm sure you know that will apply to every redirect rule then - not sure if that'll cause issues with other redirects.
(As an aside, and from memory, value allows you to set the value of _rsc to a default, but doing that also means it wont be passed on in the redirect)
Great Horned OwlOP
I can not get the :rsc value
I mean, I am not success in catching the query param value from the source and appending it to the destination
Gimme a sec - I'll spin up a local test and see what I'm getting. What version of Next.js are you using?
Also, is this happening in dev too, or just in prod?
Great Horned OwlOP
"next": "^14.2.4",
I tried a different approach:

import { NextRequest, NextResponse } from 'next/server';
import redirects from '../redirects.json';

export function middleware(request: NextRequest) {
  const url = request.nextUrl.clone();
  const { pathname } = url;
  const searchParams = url.searchParams;

  const redirect = redirects.find((r) =>
    new RegExp(`^${r.source}/?$`).test(pathname)
  );

  if (redirect) {
    url.pathname = redirect.destination;
    url.search = searchParams.toString();
    console.log('redirect: ', {
      pathname,
      search: searchParams.toString(),
      redirect,
      url: request.url,
      queryString: request.url.split('?')[1] || '',
      nextUrl: request.nextUrl,
    });
    return NextResponse.redirect(url);
  }

  return NextResponse.next();
}


Now I am surprissed because if I navigate to http://localhost:3000/blog/?_rsc=test I get redirected to http://localhost:3000/my-new-blog but without querystring!!
The querystring attribute from console.log is empty indeed,

Then trying to other query param, i.e. ?query=test that is recognized

Seems that _rsc is a reserved key. I am totally blocked...
What if you rename the param in the middleware:

  if (redirect) {
    if (searchParams.has('_rsc')) {
      const rscValue = searchParams.get('_rsc');
      searchParams.delete('_rsc');
      searchParams.set('rsc_renamed', rscValue);
    }
    url.pathname = redirect.destination;
    url.search = searchParams.toString();
    console.log('redirect: ', {
      pathname,
      search: searchParams.toString(),
      redirect,
      url: request.url,
      queryString: request.url.split('?')[1] || '',
      nextUrl: request.nextUrl,
    });
    return NextResponse.redirect(url);
  }


And then use this new query param in your blog route handler:

const MyNewBlog = () => {
  const router = useRouter();
  const { rsc_renamed } = router.query;

  // Use rsc_renamed as needed
  console.log('Renamed _rsc:', rsc_renamed);
Great Horned OwlOP
Is not possible because the request.url in middleware is not including the query param if it is names ‘_rsc’
Oh, I see. I thought it was just not passing it on 🤔
Just checking assumptions - is there a reason you're using _rsc? It's usually best practice to use a custom param like blogPostId or similar. Just wondering if the solution lies somewhere else.
Great Horned OwlOP
I didn't choose it on purpose. It's a query parameter added by Next.js to do prefetching and it returns a React server component.

Here is my problem. I deploy my website and the cache is clean. I enter any page for the first time, and it does prefetching of the URLs in the header. One of those URLs is /blog, which redirects to /my-new-blog.

The problem is that my server receives a request to /blog?_rsc=fa12, which returns a 308 to /my-new-blog (without the query parameter), and the response is a React server component, which my CDN caches. So, when I want to go to that URL, the CDN returns the React server component instead of the HTML, and it shows code like this:
Ah - gotcha, so the issue is that a call to /my-new-blog?_rsc=??? works, because that's the actual handler, but you want /blog to redirect to /my-new-blog, but for some reason, prefetch is still calling /blog from somewhere?
Great Horned OwlOP
No, the redirect destiny is /my-newiblog and the source is /blog. In the header there is a link to /blog.

Then the problem is that if the first request to /my-new-blog is done through a redirect of a prefetch from blog, then my CDN is caching the RSC instead of the HTML
and it is because the redirect is not preserving the ?_rsc query param
Out of curiosity, why link to /blog and then redirect instead of just linking to /my-new/blog? Sorry if I'm not getting it, but I'm just trying to find the very core of the issue 😊
Great Horned OwlOP
Sure; the reason is that thw web is managed by several users, who can create redirects, posts and pages. So the /blog and /my-new-blog example was a simplification. What is happening is that there someone creates a redirect and is not changing the url in the header button, or there are even posts, with its own content, that then are redirected to a different post.

So in summary, yes, this is a weird situation and probably my solution is to ensure there is no "sources" of redirects as links in my web, but on the other hand I am trying to understand why _rsc query param is not preserved in redirects
I see - that does sound like a complex problem to solve. I think this is potentially an unintended consequence and might even warrant creating an issue in the Next.js repo. I guess you could wrap the header links in a wrapper that looks up the redirects and updates the desintation programatically, but that doesn't sound like an ideal solution. And wouldn't help in the case of redirected blog URLs 🤔
Great Horned OwlOP
yes, its a weird issue that happen because humans ara unpredictable hehehehe
Murphy's law