question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Middleware `NextResponse.rewrite` 404ing when rewriting "/" with another route when deployed to Vercel

See original GitHub issue

Run next info (available from version 12.0.8 and up)

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP Mon Oct 18 19:27:44 UTC 2021
Binaries:
  Node: 14.18.0
  npm: 6.14.15
  Yarn: 1.22.17
  pnpm: 6.24.4
Relevant packages:
  next: 12.0.8-canary.18
  react: 17.0.2
  react-dom: 17.0.2

What version of Next.js are you using?

12.0.8-canary.18

What version of Node.js are you using?

14.18.0

What browser are you using?

Firefox

What operating system are you using?

Windows

How are you deploying your application?

Vercel

Describe the Bug

Using middleware, NextResponse.rewrite is not correctly rewriting a route with another existing route. Doing so results in a 404.

I am using i18n, but https://github.com/vercel/next.js/pull/31174 does not fix it.

Seems related to https://github.com/vercel/next.js/issues/30749

Here is my middleware file:

import { NextRequest, NextResponse } from "next/server";
import { CONSTANTS } from "src/utils/constants";

const ONE_YEAR_IN_MS = 3600 * 365 * 24 * 1000;

export function middleware(req: NextRequest) {
  const expires = new Date(Date.now() + ONE_YEAR_IN_MS);
  const initialVariant = req.cookies[CONSTANTS.RH_HOME_PAGE_TEST_COOKIE];

  // Redirect direct traffic to our test variant
  if (
    (!initialVariant ||
      initialVariant !== CONSTANTS.RH_HOME_PAGE_VARIANT_1_SLUG) &&
    req.nextUrl.pathname === `/${CONSTANTS.RH_HOME_PAGE_VARIANT_1_SLUG}`
  ) {
    return NextResponse.redirect(CONSTANTS.HOME_ROUTE);
  }

  // Home page AB testing
  if (req.nextUrl.pathname === CONSTANTS.HOME_ROUTE) {
    let variant = initialVariant;

    // Determine the variant via a random split
    if (!variant) {
      const split: boolean = Math.random() >= 0.5;

      variant = split
        ? CONSTANTS.RH_HOME_PAGE_VARIANT_1_SLUG
        : CONSTANTS.RH_HOME_PAGE_ORIGINAL;
    }

    const res =
      variant === CONSTANTS.RH_HOME_PAGE_ORIGINAL
        ? NextResponse.next()
        : NextResponse.redirect(`/${variant}`, 307); // Temporary redirect

    if (initialVariant !== variant) {
      res.cookie(CONSTANTS.RH_HOME_PAGE_TEST_COOKIE, variant, { expires });
    }

    return res;
  } else {
    return NextResponse.next();
  }
}

My next.config:

module.exports = {
  productionBrowserSourceMaps: true,
  outputFileTracing: false,
  images: {
    domains: [
      "images.ctfassets.net",
      "assets.ctfassets.net",
      "videos.ctfassets.net",
      "assets.zappyride.com",
    ],
  },
  i18n: {
    locales: ["en", "es"],
    defaultLocale: "en",
  },
  env: {},
  webpack: (config, options) => {
    if (!options.isServer) {
      config.resolve.alias["@sentry/node"] = "@sentry/react";
    }

    // Only add sentry webpack plugin when sentry DSN is defined and the
    // app is being deployed (NODE_ENV=production when making an optimized build
    // for a deployed environment).
    if (
      process.env.NEXT_PUBLIC_SENTRY_DSN &&
      process.env.NODE_ENV === "production"
    ) {
      config.plugins.push(
        new SentryWebpackPlugin({
          include: ".next",
          ignore: ["node_modules"],
          urlPrefix: "~/_next",
          release: process.env.VERCEL_GITHUB_COMMIT_SHA,
        })
      );
    }

    config.resolve.alias = {
      ...config.resolve.alias,
      "@mui/styled-engine": "@mui/styled-engine-sc",
    };

    return config;
  },
  async redirects() {
    if (process.env.ENVIRONMENT === "production") {
      return [...productionRedirects, ...sharedRedirects];
    } else {
      return [...sharedRedirects];
    }
  },
  async headers() {
    return [
      {
        source: "/",
        headers: [
          {
            key: "Cache-Control",
            value: "s-maxage=1, stale-while-revalidate",
          },
          ...securityHeaders,
        ],
      },
      {
        source: "/:path*",
        headers: [
          {
            key: "Cache-Control",
            value: "s-maxage=1, stale-while-revalidate",
          },
          ...securityHeaders,
        ],
      },
      {
        source: "/fonts/Averta/(.*)",
        headers: [
          {
            key: "Cache-Control",
            value: "public, max-age=31536000, stale-while-revalidate",
          },
          ...securityHeaders,
        ],
      },
      {
        source: "/fonts/fontface.css",
        headers: [
          {
            key: "Cache-Control",
            value: "public, max-age=31536000, stale-while-revalidate",
          },
          ...securityHeaders,
        ],
      },
    ];
  },
};

Expected Behavior

The rewrite should happen as it does locally on Vercel and not 404.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:3
  • Comments:21 (5 by maintainers)

github_iconTop GitHub Comments

4reactions
wadehammescommented, Jan 20, 2022

Yes works locally, not deployed.

Stack is latest Next, i18n configured with two locales, Contentful CMS. Redirect works like a charm, rewrite causes 404.

3reactions
wadehammescommented, Jan 6, 2022

@balazsorban44 seems similar. Here is my updated middleware:

import { NextRequest, NextResponse } from "next/server";
import { CONSTANTS } from "src/utils/constants";

const ONE_YEAR_IN_MS = 3600 * 365 * 24 * 1000;

export function middleware(req: NextRequest) {
  const expires = new Date(Date.now() + ONE_YEAR_IN_MS);

  // Home page AB testing
  if (req.nextUrl.pathname === "/") {
    let variant = req.cookies[CONSTANTS.RH_HOME_PAGE_TEST_COOKIE];

    // Determine the variant via a random split
    if (!variant) {
      const split: boolean = Math.random() >= 0.5; // 50/50 chance

      variant = split
        ? CONSTANTS.RH_HOME_PAGE_VARIANT_1_ROUTE
        : CONSTANTS.RH_HOME_PAGE_ORIGINAL_ROUTE;
    }

    // If variant is not original, redefine route to variant
    const route =
      variant === CONSTANTS.RH_HOME_PAGE_VARIANT_1_ROUTE
        ? `/${CONSTANTS.RH_HOME_PAGE_VARIANT_1_ROUTE}`
        : "/";

    const res = NextResponse.rewrite(route);

    res.cookie(CONSTANTS.RH_HOME_PAGE_TEST_COOKIE, variant, { expires });

    return res;
  } else {
    return NextResponse.next();
  }
}

Essentially, I am trying to perform an AB test using the original home route (“/”) and a variant (“/variant-home-refresh”). I am able to see the variant if I navigate to the route directly, however any time the original route is rewritten, the home page 404s and any links to “/” do not work throughout the site (logo, etc.).

I definitely think this could be related to i18n.

Read more comments on GitHub >

github_iconTop Results From Across the Web

next.js - Vercel Edge Functions works locally and give 404 on ...
I'm trying to work with the new edge functions from Vercel next.js and it's not working on production, always gives a 404 error....
Read more >
Rewrites - next.config.js
Rewrites allow you to map an incoming request path to a different destination path. Rewrites act as a URL proxy and mask the...
Read more >
The What, When, Why And How Of Next.js' New Middleware ...
To achieve this you can conditionally return a 404 or rewrite the request to a “blocked” page. Vercel has an example of blocking...
Read more >
Modifying Request Headers in Middleware - Vercel
Learn to add/update/delete request headers in a middleware. ... You can also set request headers in NextResponse.rewrite return NextResponse.next({ request: ...
Read more >
Vercel | Miguel Minoldo
Then we've to create the middleware.ts file in the root of src. The code here is quite simple, we get the site name...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found