i18nexus/next-i18n-router

i18nRouter overwriting original NextRequest headers and breaks Next 13 caching

Sam-Scolari opened this issue · 3 comments

Next: ^14.0.2
next-i18n-router: ^4.1.2

I am not super knowledgeable when it comes to headers and middleware but I have been having a caching issue in my Next 14 project for the last few months that is causing everything to be revalidated on request (default behavior is to never revalidate). I haven't been able to narrow the issue down until now when I noticed that a TON of headers were lost when turning my NextRequest into an i18nRequest.

I don't know if this is a bug or an intentional behavior for some reason or how I can make it not purge my headers?

middleware.ts

export function middleware(request: NextRequest) {
  console.log("--- Original Headers ---");
  request.headers.forEach((value, key) => {
    console.log(key + ": " + value);
  });

  let i18nRequest = i18nRouter(request, {
    locales: Object.keys(locales),
    defaultLocale: "en",
    routingStrategy: "dynamicSegment",
  });

  // Get the device type
  const { device } = userAgent(request);

  i18nRequest.headers.set(
    "x-device-type",
    device.type === "mobile" ? "mobile" : "desktop"
  );

  console.log("--- i18n Headers ---");
  i18nRequest.headers.forEach((value, key) => {
    console.log(key + ": " + value);
  });

  return i18nRequest;
}

Here are the headers logged prior to building the i18nRequest

--- Original Headers ---
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
cache-control: max-age=0
connection: keep-alive
host: localhost:3000
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
sec-fetch-dest: document
sec-fetch-mode: navigate
sec-fetch-site: same-origin
sec-fetch-user: ?1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
x-forwarded-for: ::1
x-forwarded-host: localhost:3000
x-forwarded-port: 3000
x-forwarded-proto: http

And here are the headers after passing through i18nRouter

--- i18n Headers ---
x-device-type: desktop
x-middleware-rewrite: http://localhost:3000/en/
x-next-i18n-router-locale: en

Probably related #29

I think there might be confusion about "request" vs "response". The i18nRouter function generates a response, not a request. So to avoid confusion, I would rename your i18nRequest variable to i18nResponse.

i18nRouter generates a response with NextResponse.next(). This response is completely new. So it will not contain any headers (besides a "x-next-i18n-router-locale" header that is added). If you want to add other headers to your response, you can do so using i18nResponse.headers.set like you did at the bottom of your function with "x-device-type".

However

I notice in the docs that the NextResponse.next() can take a request argument to forward request headers onto the response: https://nextjs.org/docs/app/api-reference/functions/next-response#next

I think this library should probably be doing this. It's beginning to sound like this is the default behavior of Next when there is no middleware involved.

We're currently looking into this. It is looking like we will likely be implementing forwarding request headers and will be released in the next version. Thanks for the report! Will keep you updated.

In the meantime, you may just want to manually add the request headers onto the i18nResponse.

Luckily this was an easy fix. Request headers are now forwarded onto the response in v5.1.0