Redirect in Next.js from uppercase to lowercase URL
Asked Answered
M

7

17

I need to redirect visitors from /Contact to /contact.

When I am doing it as described in the docs I get an infinite redirect loop.

This is what I tried:

// next.config.js
async redirects() {
    return [
        {
            source: '/Contact',
            destination: '/contact',
            permanent: true
        }
    ]
}
Multiplicand answered 1/9, 2020 at 21:34 Comment(3)
This is expected. The routing is not case sensitive. /Contact and /contact has same meaning to nextjs. So its fall into infinite loop. May i know your use case for /Contact and /contact i mean why you need to redirect this kind of url .Horseshoe
@MonzoorTamal That's not true; routing is case-sensitive; without this redirect, /Contact would give a 404 while /contact works. It's redirects that are case-insensitive. (See github.com/vercel/next.js/pull/8848, github.com/vercel/next.js/discussions/15539, reddit.com/r/nextjs/comments/hk2qmk/…).Wellesz
This does not work, because redirects (not routing) are not case-sensitive.Jordans
F
18

With Next.JS >=12 you can use custom middleware.

Create file named middleware.js under root folder and use this code:

import { NextResponse } from 'next/server';

const Middleware = (req) => {
  if (req.nextUrl.pathname === req.nextUrl.pathname.toLowerCase())
    return NextResponse.next();

  return NextResponse.redirect(new URL(req.nextUrl.origin + req.nextUrl.pathname.toLowerCase()));
};

export default Middleware;
Foxworth answered 11/11, 2021 at 13:52 Comment(5)
Do not use this method. new URL is incredibly slow.Barto
@Barto well so why their docs showing use of it nextjs.org/docs/advanced-features/middlewareFoxworth
Maybe it works fine on Vercel, but for non Vercel deployments, my site was basically unusable until I reverted it.Barto
@Barto for now problem in performance, not in URL. For now if you using redirect it doubles re-renderFoxworth
This method makes the site terribly slow, do not use it!Lacey
R
4

The following will redirect all paths that don't exist to a lower case path. If the path is not found, it will show a 404.

import { useEffect } from 'react'
import { useRouter } from 'next/router'
import Error from 'next/error'

export default function ResolveRoute() {
  const router = useRouter()

  useEffect(() => {
    const { pathname } = router;

    if (pathname !== pathname.toLowerCase()) {
      router.push(pathname.toLowerCase())
    }
  },[router])

  return <Error statusCode={404} />
}

Putting this file name as "[route].js" in the root of your pages folder will act as a catch all (unless the page exists). This will redirect to lower case. If already ready lower case then it means the page is a 404.

Ragucci answered 29/3, 2021 at 12:49 Comment(2)
This seems to always return a 404 error page, which makes sense as the return <Error ... /> is unconditional. Also note that pathname now includes params, so it you will need to apply .toLowerCase() to router.asPath. Lastly, modifying this to return null will get the user to the lower case path, but will not fix any issue with SEO, which would require a 301/302 redirect.Yelena
@Yelena This is a catch all. If a route exists then this component will not be called. If the route doesn't exist, this component will be called. If the pathname is not in lowercase then it will redirect to lowercase path (and never reach the "return <Error />" line). If the pathname is already in lowercase, the route simply doesn't exist and an error is returned.Ragucci
B
1

Following this suggestion from Reddit https://www.reddit.com/r/nextjs/comments/hk2qmk/nextjs_routing_case_sensitivity_issue/fwt29n2?utm_source=share&utm_medium=web2x&context=3

I fixed it using the _error.js page component. Like this:

import { hasUpperCase } from '../lib/string';

...

Error.getInitialProps = ({ asPath, err, res }) => {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404;

  if (asPath && hasUpperCase(asPath)) {
    res.writeHead(307, { Location: asPath.toLowerCase() });
    res.end();
  }

  return { statusCode };
};

export default Error;

I would also prefer doing this with redirects like your example though.

Bochum answered 26/8, 2021 at 17:7 Comment(1)
I think this is the best way to handle this redirection problems.. the middleware approach makes the site slow and the next config approach doesnt let you control the case sensitivenessLacey
H
0

When I ran into this problem, I noticed NextJS appends a trailing slash to the paths. When I looked at my network console in the browser, I saw requests with alternating status codes of 307 and 308. Once I added a trailing slash to the destination path, I saw a lot of requests with a status of 307, but still getting errors.

Once I added a trailing slash to both the source and destination paths, the redirect happened as expected:

// next.config.js
async redirects() {
    return [
        {
            source: '/Contact/',
            destination: '/contact/',
            permanent: true
        }
    ]
}
Hawkweed answered 31/8, 2021 at 18:11 Comment(1)
This is the only correct answer. Why is it downvoted?Dart
T
0

I found this helpful link that cover many solution for this issue: https://www.youtube.com/watch?v=_vSMITiXAik Solution 1:

  1. use rewirte function in your next.config.js
    return [
      {
        source: "(c|C)(o|O)(n|N)(t|T)(a|A)(c|C)(t|T)",
        destination: "/Contact", // here you need to add the exact page contact or Contact
      },
    ];
  },
  1. use the _middleware feature: inside you pages folder add a _middleware.ts file.
import { NextRequest, NextResponse } from "next/server";

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname === request.nextUrl.pathname.toLocaleLowerCase())
    return NextResponse.next();
  return NextResponse.redirect(
    `${request.nextUrl.origin}${request.nextUrl.pathname.toLocaleLowerCase()}`
  );
}

with this solution you can need to rename your page to be lowercase.

  1. use middleware feature with a folder for each page.

you need to keep the _middleware.ts the same and you need to do those steps:

  • create a folder contact all lowercase.
  • move your page inside this folder.
  • add an index.ts that point to your page. its conent should be something like this:
export { default } from "./contact";
  • rename all your page with this extension: .page.tsx or page.ts if you use typescript and .page.jsx or page.js if javascript.
  • in next.config.js you need to add this pageExtensions: ["page.tsx", "page.ts"] if you use typescript and pageExtensions: ["page.jsx", "page.js"] if you use javascript.

you need to do this for all your pages.

Ticktack answered 30/1, 2022 at 16:24 Comment(0)
F
0

Here's a very elegant solution that will also carry over any search/query params:

import { NextResponse } from 'next/server';

const Middleware = (req) => {
  if (req.nextUrl.pathname !== req.nextUrl.pathname.toLowerCase()) {
    const url = req.nextUrl.clone()
    url.pathname = url.pathname.toLowerCase()
    return NextResponse.redirect(url)
  }
  return NextResponse.next();
};

export default Middleware;
Foment answered 13/5, 2022 at 23:45 Comment(1)
middleware solution = slow siteLacey
A
0

While I enjoyed Andrew's response, it's on client side, and I would much more prefer a server side solution.

Here is what I did inspired by Andrew's comment:

import Custom404 from "./404";

export default function CatchAllRoutes({url}) {
    return <Custom404 />
}

export async function getServerSideProps(context) {
    const url = context.query.catch;
    const { res } = context;
    if (url !== url.toLowerCase()) {
        res.writeHead(307, {location: `/${url.toLowerCase()}`}) // 307 is temporary while 308 is permanent, choose your preference here..
        res.end();
    } 
    else {
        context.res.statusCode = 404;
    }
    return { props: { url } }; // Note: You don't need a prop, but the name of function requires one..
}

I named the file [catch].js at the root of my pages. If the URL does not match any page in your project, and the URL is not lowercased, it lowercases it and redirects there, otherwise we know it's a 404, in which case we give status code 404 and render the custom404 component.

Allister answered 25/7, 2023 at 14:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.