vercel / next.js

The React Framework
https://nextjs.org
MIT License
127.04k stars 27k forks source link

12.2.6-canary.10 displaying JSON in browser instead of building page html #40241

Closed wadehammes closed 2 years ago

wadehammes commented 2 years ago

Verify canary release

Provide environment information

Operating System: Platform: darwin Arch: arm64 Version: Darwin Kernel Version 21.6.0: Wed Aug 10 14:28:23 PDT 2022; root:xnu-8020.141.5~2/RELEASE_ARM64_T6000 Binaries: Node: 16.15.0 npm: 8.5.5 Yarn: 1.22.19 pnpm: N/A Relevant packages: next: 12.2.6-canary.9 eslint-config-next: 12.2.6-canary.9 react: 18.2.0 react-dom: 18.2.0

What browser are you using? (if relevant)

Firefox

How are you deploying your application? (if relevant)

Vercel

Describe the Bug

When deploying to Vercel with the latest canary, visiting pages will sometimes flash the styled output and then display the page JSON in the browser:

Screenshot 2022-09-05 at 10 18 40 PM

Expected Behavior

Page shoudl rendered with styled HTML.

Link to reproduction

https://rhythm-marketing-git-update-next1226-gotrhythm.vercel.app/

To Reproduce

Standard Next.js setup with i18n and basic middleware rewrite usage.

balazsorban44 commented 2 years ago

Some browser extensions (e.g. JSON viewer) might cause this issue. Can you verify it's not the case? (I.e. try opening the page in a different browser/incognito, making sure the extension is disabled). I cannot look at your deployment because it's password protected, but having a look at the code would be more useful anyway. Can you link your repository/reproduction, please? :pray:

wadehammes commented 2 years ago

Unfortunately it is all private and I do not have time to complete a reproduction today. I've never seen this happen before in my time using Next.js. Current stable of our site is running 12.2.5 with no issues.

I'm seeing this happen in all browsers, private (no extensions) and regular. Seems to shift to JSON view if I do a hard refresh (cmd+shift+r)

@balazsorban44

wadehammes commented 2 years ago

I could paste some files here if it helps?

next.config.js

const { withSentryConfig } = require("@sentry/nextjs");

const sentryWebpackPluginOptions = {
  // Additional config options for the Sentry Webpack plugin. Keep in mind that
  // the following options are set automatically, and overriding them is not
  // recommended:
  //   release, url, org, project, authToken, configFile, stripPrefix,
  //   urlPrefix, include, ignore

  silent: true, // Suppresses all logs
  // For all available options, see:
  // https://github.com/getsentry/sentry-webpack-plugin#options.
};

const withTM = require("next-transpile-modules")([
  "@mui/material",
  "@mui/system",
]);

module.exports = withSentryConfig(
  withTM(
    {
      reactStrictMode: true,
      productionBrowserSourceMaps: true,
      outputFileTracing: false,
      swcMinify: true,
      compiler: {
        // ssr and displayName are configured by default
        styledComponents: true,
        reactRemoveProperties: false,
      },
      images: {
        domains: [
          "images.ctfassets.net",
          "assets.ctfassets.net",
          "videos.ctfassets.net",
          "assets.zappyride.com",
        ],
      },
      i18n: {
        locales: ["en", "es"],
        defaultLocale: "en",
      },
      sentry: {
        disableClientWebpackPlugin: true,
        disableServerWebpackPlugin: true,
      },
      env: {
        CONTENTFUL_SPACE_ID: process.env.CONTENTFUL_SPACE_ID,
        CONTENTFUL_API_TOKEN: process.env.CONTENTFUL_API_TOKEN,
        CONTENTFUL_PREVIEW_TOKEN: process.env.CONTENTFUL_PREVIEW_TOKEN,
        CONTENTFUL_PREVIEW_SECRET: process.env.CONTENTFUL_PREVIEW_SECRET,
        ENVIRONMENT: process.env.ENVIRONMENT,
        GA_TRACKING_ID: process.env.GA_TRACKING_ID,
        GA_GTM_PREVIEW_ENV: process.env.GA_GTM_PREVIEW_ENV,
        GA_GTM_AUTH: process.env.GA_GTM_AUTH,
        NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
        SEGMENT_ANALYTICS_KEY: process.env.SEGMENT_ANALYTICS_KEY,
        SENTRY_TRACES_SAMPLE_RATE: process.env.SENTRY_TRACES_SAMPLE_RATE,
        SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN,
        VERCEL_GITHUB_COMMIT_SHA: process.env.VERCEL_GITHUB_COMMIT_SHA,
        FULLSTORY_ORG_ID: process.env.FULLSTORY_ORG_ID,
        API_SECRET: process.env.API_SECRET,
        EMBED_SOCIAL_API_KEY: process.env.EMBED_SOCIAL_API_KEY,
        VERCEL_TEAM_ID: process.env.VERCEL_TEAM_ID,
        VERCEL_API_TOKEN: process.env.VERCEL_API_TOKEN,
        SMARTY_STREETS_WEBKEY: process.env.SMARTY_STREETS_WEBKEY,
        SMARTY_STREETS_AUTH_ID: process.env.SMARTY_STREETS_AUTH_ID,
        SMARTY_STREETS_AUTH_TOKEN: process.env.SMARTY_STREETS_AUTH_TOKEN,
        ZAPPY_RIDE_API_AUTH_KEY: process.env.ZAPPY_RIDE_API_AUTH_KEY,
        LAUNCH_DARKLY_CLIENT_ID: process.env.LAUNCH_DARKLY_CLIENT_ID,
      },
      webpack: (config, options) => {
        if (options.isServer && options.nextRuntime !== "nodejs") return config;

        config.module.rules.push({
          test: /\.svg$/,
          use: ["@svgr/webpack"],
        });

        return config;
      },
      async redirects() {
        if (process.env.ENVIRONMENT === "production") {
          return [
            ...productionRedirects,
            ...sharedRedirects,
            ...oldCampaignRedirects,
          ];
        } else {
          return [...sharedRedirects, ...oldCampaignRedirects];
        }
      },
      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-font/(.*)",
            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,
            ],
          },
        ];
      },
    },
    sentryWebpackPluginOptions
  )
);

// Redirect test and home slug pages on Production
const sources = [
  "/:parent(test-page.*)",
  "/:parent(home.*)",
  "/partner/:slug(test-page.*)",
  "/affiliate/:slug(test-page.*)",
  "/lp/:slug(test-page.*)",
  "/page-preview",
  "/deployments",
  "/hello",
];

const productionRedirects = sources.map((source) => ({
  source,
  destination: "/",
  permanent: true,
}));

const oldCampaignUrls = [
  "/bingo",
  "/breathe",
  "/hardtospell-2",
  "/visa",
  "/cinemark",
];

const oldCampaignRedirects = oldCampaignUrls.map((source) => ({
  source,
  destination: "/",
  permanent: true,
}));

// Redirects various lp directories to home page since they have no page
const lpSources = ["/partner", "/affiliate", "/lp"];

const lpRedirects = lpSources.map((source) => ({
  source,
  destination: "/",
  permanent: true,
}));

const error404Redirects = [
  {
    // 301 Redirect /eswelcome 404
    source: "/eswelcome",
    destination: "/es/welcome",
    permanent: true,
  },
  {
    // 301 Redirect /eswelcome 404
    source: "/es/eswelcome",
    destination: "/es/welcome",
    permanent: true,
  },
  {
    // 301 Redirect /hc/en-us 404
    source: "/hc/en-us",
    destination: "/",
    permanent: true,
  },
  {
    source: "/summer-email",
    destination: "/lp/email-summer",
    permanent: true,
  },
  {
    source: "/email-summer",
    destination: "/lp/email-summer",
    permanent: true,
  },
  {
    source: "/lp/solar-buyback-12",
    destination: "/rooftop-solar-buyback-plan",
    permanent: true,
  },
  {
    source: "/about/and",
    destination: "/about",
    permanent: true,
  },
  {
    source: "/about/a",
    destination: "/about",
    permanent: true,
  },
];

// Redirects needed for both Production and Staging/Development
const sharedRedirects = [
  {
    // 301 Redirect ElectricityPlans page to new affiliate url
    source: "/ep",
    destination: "/affiliate/ep",
    permanent: true,
  },
  {
    // 301 Redirect original simple header page to new lp url
    source: "/texas-sh",
    destination: "/lp/texas-sh",
    permanent: true,
  },
  {
    // 301 Redirect Support pages
    source: "/support",
    destination: "https://support.gotrhythm.com",
    permanent: true,
  },
  {
    // 301 Redirect register pages
    source: "/register",
    destination: "https://app.gotrhythm.com/sign-up?rcid=default",
    permanent: true,
  },
  {
    // 301 Redirect login page
    source: "/login",
    destination: "https://app.gotrhythm.com/sign-in",
    permanent: true,
  },
  {
    // 301 Redirect login page
    source: "/sign-in",
    destination: "https://app.gotrhythm.com/sign-in",
    permanent: true,
  },
  {
    // 301 Redirect login page
    source: "/account",
    destination: "https://app.gotrhythm.com/sign-in",
    permanent: true,
  },
  {
    // 301 Redirect login page
    source: "/myaccount",
    destination: "https://app.gotrhythm.com/sign-in",
    permanent: true,
  },
  {
    // 301 Redirect login page
    source: "/my-account",
    destination: "https://app.gotrhythm.com/sign-in",
    permanent: true,
  },
  {
    // 301 Redirect register pages
    source: "/sign-up",
    destination: "https://app.gotrhythm.com/sign-up?rcid=default",
    permanent: true,
  },
  {
    // 301 Redirect register pages
    source: "/sign-up/plans",
    destination: "https://app.gotrhythm.com/sign-up?rcid=default",
    permanent: true,
  },
  {
    // 301 Redirect /payments 404
    source: "/payments",
    destination: "https://app.gotrhythm.com/sign-in",
    permanent: true,
  },
  {
    // 301 Redirect /payments 404
    source: "/pay-bill",
    destination: "https://app.gotrhythm.com/sign-in",
    permanent: true,
  },
  {
    // 301 Redirect Earth Day
    source: "/lp/texas-earth-day",
    destination: "/electricity/texas",
    permanent: true,
  },
  {
    // 301 Redirect Earth Day
    source: "/lp/fb-earth-day",
    destination: "/electricity/texas",
    permanent: true,
  },
  {
    // 301 Redirect /reviews 404
    source: "/reviews",
    destination: "/about/reviews",
    permanent: true,
  },
  {
    // 301 Redirect /chapman-ranch
    source: "/chapman-ranch",
    destination: "/chapman-ranch-wind-energy-plans-texas",
    permanent: true,
  },
  {
    // 301 Redirect /solar
    source: "/solar",
    destination: "/solar-electricity-plans",
    permanent: true,
  },
  {
    // 301 Redirect /solar-buyback
    source: "/solar-buyback",
    destination: "/rooftop-solar-buyback-plan",
    permanent: true,
  },
  {
    // 301 Redirect /careers
    source: "/careers",
    destination: "/about/careers",
    permanent: true,
  },
  {
    // 301 Redirect /jobs
    source: "/jobs",
    destination: "/about/careers",
    permanent: true,
  },
  {
    // 301 Redirect /rewards 404
    source: "/why-rhythm/rewards",
    destination: "/rewards",
    permanent: true,
  },
  {
    // Redirect /purchase to /purchase/new-vehicles
    source: "/ev/purchase",
    destination: "/ev/purchase/new-vehicles",
    permanent: true,
  },
  {
    // Redirect /terms to /terms-of-use
    source: "/terms",
    destination: "/terms-of-use",
    permanent: true,
  },
  {
    // Redirect /ev/power to /electric-vehicle-plans
    source: "/ev/power",
    destination: "/electric-vehicle-plans",
    permanent: true,
  },
  {
    // Redirect old plans page to new plans overview
    source: "/why-rhythm/electricity-plans",
    destination: "/plans-overview",
    permanent: true,
  },
  {
    // Redirect old home solar buyback to new rooftop solar buyback
    source: "/solar-buyback-plan",
    destination: "/rooftop-solar-buyback-plan",
    permanent: true,
  },
  {
    // Redirect all /electricity SEO pages (including city pages) to new /texas-electricity
    source: "/electricity(.*)",
    destination: "/texas-electricity",
    permanent: true,
  },
  {
    // Redirect all /vs competitor pages to /compare
    source: "/vs(.*)",
    destination: "/compare-texas-electricity-providers",
    permanent: true,
  },
  {
    // Redirect all /mymove to home
    source: "/mymove",
    destination: "/",
    permanent: true,
  },
  {
    source: "/secure",
    destination:
      "/secure2022?utm_source=external-mktg&utm_medium=DM&utm_campaign=ADM_SecureMover_07_2022&utm_content=secure-move-july2022&utm_term=20220705&rcid=simplisafe",
    permanent: true,
  },
  {
    // Redirect /simply-move to new simply-move with query and rcid param
    source: "/simply-move",
    destination:
      "/simply-move2022?rcid=mover-promo-200&utm_content=simplymove-july2022&utm_term=20220705&utm_source=external-mktg&utm_medium=DM&utm_campaign=ADM_SimplyMove200_07_2022",
    permanent: true,
  },
  {
    // Redirect /movers to new movers2022 with query and rcid param
    source: "/movers",
    destination:
      "/movers2022?utm_source=external-mktg&utm_medium=MyMoveDigital&utm_campaign=AWM_MyMoveDigital_07_2022&utm_content=mymove-july2022&utm_term=20220701&rcid=simplisafe",
    permanent: true,
  },
  ...error404Redirects,
  ...lpRedirects,
];

// https://securityheaders.com
const scriptSrc = [
  "'self'",
  "'unsafe-eval'",
  "'unsafe-inline'",
  "*.youtube.com",
  "*.ads-twitter.com",
  "*.twitter.com",
  "*.instagram.com",
  "*.ctfassets.net",
  "*.fullstory.com",
  "*.zdassets.com",
  "*.segment.com",
  "*.facebook.net",
  "*.nextdoor.com",
  "*.tvsquared.com",
  "*.doubleclick.net",
  "*.adsrvr.org",
  "*.bing.com",
  "*.google.com",
  "*.google-analytics.com",
  "*.googletagmanager.com",
  "*.googleadservices.com",
  "*.googleusercontent.com",
  "polyfill.io",
  "*.vercel-insights.com",
  "*.vercel.app",
  "embedsocial.com",
  "*.abtasty.com",
  "*.smooch.io",
  "*.mypurecloud.com",
  "*.cloudfront.net",
  "*.cobrowse.io",
  "*.redditstatic.com",
  "*.clarity.ms",
  "aa.trkn.us",
  "*.hotjar.com",
  "*.adnxs.com",
  "*.shop.pe",
  "shop.pe",
  "addshoppers.s3.amazonaws.com",
  "cdn.id5-sync.com",
  "action.dstillery.com",
  "action.media6degrees.com",
];

const ContentSecurityPolicy = `
  default-src 'self';
  script-src ${scriptSrc.join(" ")};
  child-src *.youtube.com *.google.com *.twitter.com *.facebook.com *.adsrvr.org *.doubleclick.net embedsocial.com *.abtasty.com;
  style-src 'self' 'unsafe-inline' *.googleapis.com *.google.com *.googletagmanager.com embedsocial.com *.abtasty.com;
  img-src * blob: data: *.ctfassets.net *.fbsbx.com *.googleusercontent.com *.abtasty.com smart-pixl.com *.fullstory.com;
  object-src * blob: data:;
  media-src 'self' *.zdassets.com *.ctfassets.net *.abtasty.com;
  connect-src *;
  frame-src * 'self' blob: data: *.ctfassets.net *.abtasty.com;
  font-src 'self' data: fonts.gstatic.com *.abtasty.com *.cloudflare.com;
  worker-src 'self' *.vercel.app;
  manifest-src 'self' *.vercel.app;
`;

const setAccessControlOrigin = () => {
  if (process.env.ENVIRONMENT === "production") {
    return "https://www.gotrhythm.com";
  } else if (process.env.ENVIRONMENT === "staging") {
    return "https://www.staging.gotrhythm.com";
  } else {
    return "http://localhost:1337";
  }
};

const securityHeaders = [
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
  {
    key: "Content-Security-Policy",
    value: ContentSecurityPolicy.replace(/\n/g, ""),
  },
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
  {
    key: "Referrer-Policy",
    value: "strict-origin-when-cross-origin",
  },
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
  {
    key: "X-Frame-Options",
    value: "SAMEORIGIN",
  },
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
  {
    key: "X-Content-Type-Options",
    value: "nosniff",
  },
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control
  {
    key: "X-DNS-Prefetch-Control",
    value: "on",
  },
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
  {
    key: "Strict-Transport-Security",
    value: "max-age=31536000; includeSubDomains; preload",
  },
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
  {
    key: "Permissions-Policy",
    value: "camera=(), microphone=(), geolocation=(), interest-cohort=()",
  },
  {
    key: "Access-Control-Allow-Origin",
    value: setAccessControlOrigin(),
  },
  {
    key: "Vary",
    value: "Origin",
  },
];

middleware.ts

import isbot from "isbot";
import { type NextRequest, NextResponse, userAgent } from "next/server";
import {
  fetchFeatureFlag,
  MarketingHomeAbTestVariantValues,
} from "src/api/fetchFeatureFlag";
import { CONSTANTS } from "src/utils/constants";
import { v4 as uuid } from "uuid";

isbot.extend(["/DatadogSynthetics/"]);

const PUBLIC_FILE = /\.(.*)$/;

export async function middleware(request: NextRequest) {
  const nextPage = NextResponse.next();

  const { url, headers } = request;
  const { ua } = userAgent(request);
  const { pathname, basePath, origin, locale, search } = request.nextUrl;

  const expires = new Date(Date.now() + 2592000000); // one month expiration

  const homeUrl = new URL(`${basePath}/${locale}${search}`, origin).toString();
  const twoMonthsFreePlanUrl = new URL(
    `${basePath}/${locale}/${CONSTANTS.TWO_MONTHS_FREE_PLAN_SLUG}${search}`,
    origin
  );

  const isTwoMonthsFreePlanAdLander =
    pathname.includes(
      CONSTANTS.TWO_MONTHS_FREE_PLAN_FREE_INFLATION_RELIEF_LANDER
    ) || pathname.includes(CONSTANTS.TWO_MONTHS_FREE_PLAN_FREE_IS_GOOD_LANDER);

  const referer = headers.get("Referer") || "";

  const isNavigatingFromTwoMonthsFreePlanAdLander =
    referer.includes(CONSTANTS.TWO_MONTHS_FREE_PLAN_FREE_IS_GOOD_LANDER) ||
    referer.includes(
      CONSTANTS.TWO_MONTHS_FREE_PLAN_FREE_INFLATION_RELIEF_LANDER
    );

  if (
    pathname.startsWith("/_next") || // exclude Next.js internals
    pathname.startsWith("/api") || //  exclude all API routes
    pathname.startsWith("/static") || // exclude static files
    PUBLIC_FILE.test(pathname) // exclude all files in the public folder
  ) {
    return nextPage;
  }

  // Setup rhFeatureFlagUserId
  // this will run on all paths of the site, no matter
  // where a visitor enters, to ensure it gets set up
  let rhFeatureFlagUserId = request.cookies.get(
    CONSTANTS.RH_FEATURE_FLAG_USER_ID_COOKIE
  );

  if (!rhFeatureFlagUserId) {
    rhFeatureFlagUserId = `rh_${uuid()}`;

    nextPage.cookies.set(
      CONSTANTS.RH_FEATURE_FLAG_USER_ID_COOKIE,
      rhFeatureFlagUserId,
      {
        expires,
        sameSite: "strict",
      }
    );
  }

  // If pathname is not lowercase, redirect
  if (pathname !== pathname.toLowerCase()) {
    const lowercaseRedirect = NextResponse.redirect(
      new URL(`${pathname.toLowerCase()}${search}`, url)
    );

    lowercaseRedirect.cookies.set(
      CONSTANTS.RH_FEATURE_FLAG_USER_ID_COOKIE,
      rhFeatureFlagUserId,
      {
        expires,
        sameSite: "strict",
      }
    );

    return lowercaseRedirect;
  }

  // Redirect any direct traffic to our home test variant back to home page
  if (pathname.startsWith("/variant-home")) {
    const homeRedirect = NextResponse.redirect(homeUrl);

    homeRedirect.cookies.set(
      CONSTANTS.RH_FEATURE_FLAG_USER_ID_COOKIE,
      rhFeatureFlagUserId,
      {
        expires,
        sameSite: "strict",
      }
    );

    return homeRedirect;
  }

  // Homepage AB Test Setup
  if (
    pathname === CONSTANTS.HOME_ROUTE &&
    !isbot(ua) &&
    !isNavigatingFromTwoMonthsFreePlanAdLander
  ) {
    // Launch Darkly flag: marketing-home-ab-test-multiple-variants
    // If flag is off, defaults to "original" (control home page)
    const marketingHomeAbTestVariantSlug = await fetchFeatureFlag<string>({
      featureFlagUserId: rhFeatureFlagUserId,
      flag: "marketing-home-ab-test-multiple-variants",
      defaultValue: MarketingHomeAbTestVariantValues.Original,
    });

    if (marketingHomeAbTestVariantSlug) {
      const homeTestPage =
        marketingHomeAbTestVariantSlug !==
        MarketingHomeAbTestVariantValues.Original
          ? NextResponse.rewrite(
              new URL(
                `${basePath}/${locale}/${marketingHomeAbTestVariantSlug}${search}`,
                origin
              ).toString()
            )
          : NextResponse.next();

      // Make sure we add our feature flag user id to cookies
      // since we reset the NextResponse to a new const
      homeTestPage.cookies.set(
        CONSTANTS.RH_FEATURE_FLAG_USER_ID_COOKIE,
        rhFeatureFlagUserId,
        {
          expires,
          sameSite: "strict",
        }
      );

      homeTestPage.cookies.set(
        CONSTANTS.RH_HOME_PAGE_TEST_COOKIE,
        marketingHomeAbTestVariantSlug,
        {
          expires,
          sameSite: "strict",
        }
      );

      return homeTestPage;
    }

    return nextPage;
  }

  // Redirect any direct traffic to our two months free plan test variant back to control page
  if (pathname.startsWith(`/${CONSTANTS.TWO_MONTHS_FREE_PLAN_VARIANT_SLUG}`)) {
    const twoMonthsFreePlanRedirect =
      NextResponse.redirect(twoMonthsFreePlanUrl);

    twoMonthsFreePlanRedirect.cookies.set(
      CONSTANTS.RH_FEATURE_FLAG_USER_ID_COOKIE,
      rhFeatureFlagUserId,
      {
        expires,
        sameSite: "strict",
      }
    );

    return twoMonthsFreePlanRedirect;
  }

  // Two Free Months AB Test Setup
  if (
    pathname.startsWith(`/${CONSTANTS.TWO_MONTHS_FREE_PLAN_SLUG}`) &&
    !isTwoMonthsFreePlanAdLander &&
    !isbot(ua)
  ) {
    const homePageTestSlug = await fetchFeatureFlag<string>({
      featureFlagUserId: rhFeatureFlagUserId,
      flag: "marketing-home-ab-test-multiple-variants",
      defaultValue: MarketingHomeAbTestVariantValues.Original,
    });

    if (homePageTestSlug === MarketingHomeAbTestVariantValues.Original) {
      // Launch Darkly flag: marketing-two-free-month-plan
      // If flag is off, defaults to "two-months-free-plan"
      const marketingTwoMonthsFreePlanSlug = await fetchFeatureFlag<string>({
        featureFlagUserId: rhFeatureFlagUserId,
        flag: "marketing-two-free-month-plan",
        defaultValue: CONSTANTS.TWO_MONTHS_FREE_PLAN_SLUG,
      });

      if (marketingTwoMonthsFreePlanSlug) {
        const twoMonthsFreeTestPage =
          marketingTwoMonthsFreePlanSlug !== CONSTANTS.TWO_MONTHS_FREE_PLAN_SLUG
            ? NextResponse.rewrite(
                new URL(
                  `${basePath}/${locale}/${marketingTwoMonthsFreePlanSlug}${search}`,
                  origin
                ).toString()
              )
            : NextResponse.next();

        // Make sure we add our feature flag user id to cookies
        // since we reset the NextResponse to a new const
        twoMonthsFreeTestPage.cookies.set(
          CONSTANTS.RH_FEATURE_FLAG_USER_ID_COOKIE,
          rhFeatureFlagUserId,
          {
            expires,
            sameSite: "strict",
          }
        );

        twoMonthsFreeTestPage.cookies.set(
          CONSTANTS.RH_TWO_MONTHS_FREE_PLAN_COOKIE,
          marketingTwoMonthsFreePlanSlug,
          {
            expires,
            sameSite: "strict",
          }
        );

        return twoMonthsFreeTestPage;
      }

      return nextPage;
    }

    if (homePageTestSlug === CONSTANTS.RH_HOME_PAGE_VARIANT_B_SLUG) {
      const twoMonthsFreePlanVariantBPage = NextResponse.rewrite(
        new URL(
          `${basePath}/${locale}/${CONSTANTS.TWO_MONTHS_FREE_PLAN_VARIANT_SLUG}${search}`,
          origin
        ).toString()
      );

      // Make sure we add our feature flag user id to cookies
      // since we reset the NextResponse to a new const
      twoMonthsFreePlanVariantBPage.cookies.set(
        CONSTANTS.RH_FEATURE_FLAG_USER_ID_COOKIE,
        rhFeatureFlagUserId,
        {
          expires,
          sameSite: "strict",
        }
      );

      twoMonthsFreePlanVariantBPage.cookies.set(
        CONSTANTS.RH_TWO_MONTHS_FREE_PLAN_COOKIE,
        CONSTANTS.TWO_MONTHS_FREE_PLAN_VARIANT_SLUG,
        {
          expires,
          sameSite: "strict",
        }
      );

      return twoMonthsFreePlanVariantBPage;
    }

    return nextPage;
  }

  return nextPage;
}

These are the only files where I could see something causing this, although like I said, 12.2.5 works fine (as seen on https://www.gotrhythm.com) running these same files.

balazsorban44 commented 2 years ago

Could you verify which canary caused the issue first? Sounds like candidates are 12.2.6-canary.0 up to 12.2.6-canary.10. If it's a bug, that would help us track down the exact PR that introduced this.

Also, just to confirm:

When deploying to Vercel with the latest canary

So does this only happen when deployed, or in next dev/next build && next start also?

jakejarvis commented 2 years ago

Experiencing this on 12.2.6-canary.10 as well — will follow up with a reproduction.

edit: in the meantime, https://jarvis-edvwwdw43-jakejarvis.vercel.app/projects/ edit 2: URL above just fixed itself. hmm... edit 3: I've traced it backwards and it looks like something introduced in canary.7 is causing this — possibly #39902 or #40076? Still investigating. :)

Screen Shot 2022-09-07 at 2 55 43 PM
sdornan commented 2 years ago

This issue still seems to occur with canary.11.

wadehammes commented 2 years ago

@jakejarvis thanks for getting to this before I had a chance today!

ijjk commented 2 years ago

Hi, this should now be patched in v12.2.6-canary.12of Next.js

github-actions[bot] commented 2 years ago

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.