platforms icon indicating copy to clipboard operation
platforms copied to clipboard

Nested path inside _sites is not working

Open tandat2209 opened this issue 3 years ago • 9 comments

I tried to put the specific path for custom domains, like below

my-domain.localhost:3000/posts/nextjs-awesome -> returns 404

My project | _sites | ----posts |---index.tsx |----[slug].tsx

It does not work. Can someone tell me why?

tandat2209 avatar Jul 23 '22 17:07 tandat2209

I believe this is because of

url.pathname = `/_sites/${currentHost}${url.pathname}`;

inside of middleware.ts. The url.pathname includes a / so the pathname ends being something like pathname: '/_sites//fcs' and that double / messes things up.

JamesSingleton avatar Jul 23 '22 23:07 JamesSingleton

I think the matcher pattern that's used (src) to exclude paths with . is also excluding paths that contain any subsequent / chars after the prefix /.

This should work (note the regular expression doesn't contain / anymore in the negated set):

export const config = {
  matcher: [
    "/",
    "/([^.]*)", // exclude `/public` files by matching all paths except for paths containing `.` (e.g. /logo.png)
    "/site/:path*",
    "/post/:path*",
    "/_sites/:path*",
  ],
};

dstotijn avatar Jul 23 '22 23:07 dstotijn

@dstotijn doing that for me broke images loading in Next.js' Image component

JamesSingleton avatar Jul 23 '22 23:07 JamesSingleton

That regular expression I proposed doesn't narrow down "public" files, it filters out any path with . in it. So it does what it says in the comment ;) You could write a regexp with a negative lookahead to filter out whatever patterns that you know occur in static file paths, e.g. .png, .jpg, etcetera.

dstotijn avatar Jul 23 '22 23:07 dstotijn

Hey guys, sorry for the late response!

@tandat2209 The reason this is happening is because you need to add an extra matcher item "/posts/:path*" in the matcher config in the middleware file, if that's what you want your route structure to look like. The reason we do this is to make sure the middleware only matches paths that it needs to and does not end up matching absolute paths like "demo.vercel.pub/_sites/steven" and have the content from steven be served.

@dstotijn @JamesSingleton hope this made sense for y'all as well! I also added some detailed comments here explaining everything: https://github.com/vercel/platforms/blob/main/middleware.ts#L3-L28

Will close the issue for now, feel free to reopen it if there's any other confusions! :)

steven-tey avatar Aug 12 '22 15:08 steven-tey

@steven-tey, its not clear to me if its possible (and how) to match nested routes. I have different first level domain that points to the multi-tenancy app, and need to fetch all url paths, (using a catch all route [...slug].tsx) thats my code :

 import { NextRequest, NextResponse } from 'next/server';
const env = process.env.NODE_ENV;

export const config = {
  matcher: [
    '/',
    '/([^/.]*)', // exclude `/public` files by matching all paths except for paths containing `.` (e.g. /logo.png)
    '/site/:path*',
    '/post/:path*',
    '/_sites/:path*',
  ],
};

export default function middleware(req: NextRequest) {
  const url = req.nextUrl;
  const hostname = req.headers.get('host') || 'localhost:3000';
  // const currentHost = env === 'development' ? 'localhost' : hostname;
  const currentHost = hostname.replace(':3001', '');
  url.pathname = `/_sites/${currentHost}${url.pathname}`;
  console.log(`/_sites/${currentHost}${url.pathname}`);
  return NextResponse.rewrite(url);
}

For example, mywebsite.com/hello works while mywebsite.com/hello/there does not. Any idea on how i can match nested routes?

nicosh avatar Aug 26 '22 06:08 nicosh

Facing the same issue!

DevOfManyThings avatar Sep 14 '22 22:09 DevOfManyThings

@tandat2209 @JamesSingleton @dstotijn @nicosh @DevOfManyThings – in light of my recent discovery that you can use negative matchers for middleware, I just pushed a new version of the middleware that uses negative matchers to match all request paths except:

  1. /api routes
  2. /_next (Next.js internals)
  3. /fonts (inside /public folder)
  4. /examples (inside /public folder)
  5. all root files inside /public folder(e.g. /favicon.ico)

This should fix all of the issues above! :)

steven-tey avatar Sep 15 '22 01:09 steven-tey

@steven-tey this sound great! however, for my use case, i ended up using conditional statements

export default function middleware(req: NextRequest) {
  if (
    !req.nextUrl.pathname.includes('.') &&
    !req.nextUrl.pathname.includes('/api/revalidate') &&
    !req.nextUrl.pathname.includes('/_next')
  ) {
    const url = req.nextUrl;
    const hostname = req.headers.get('host') || 'localhost:3000';
    // const currentHost = env === 'development' ? 'localhost' : hostname;
    const currentHost = hostname.replace(':3001', '');
    //console.log(`${currentHost}${url.pathname}`);

    url.pathname = `/_sites/${currentHost}${url.pathname}`;
    //console.log(`/_sites/${currentHost}${url.pathname}`);
    return NextResponse.rewrite(url);
  }
}


and changed [slug].tsx to [...slug].tsx

nicosh avatar Sep 15 '22 07:09 nicosh