vercel / next.js

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

trailing slash in link for legit page works for client side navigation but leads to not found bundle and 404 on hard refresh (ssr) #5214

Closed iamstarkov closed 4 years ago

iamstarkov commented 6 years ago

trailing slash in link for legit page works for client side navigation but leads to not found bundle and 404 on hard refresh (ssr)

Bug report

Describe the bug

let me know if title needs further clarification.

all relevant issues has been closed with reasoning that its been fixed in 6-canary (I believe it is not) or by improved serve (which is true only in perhaps production static export).

I'm rewriting my existing blog to next.js and i previously used trailing slashes. Latest serve can help with it once i build my next.js powered blog. But in order to fix dev env i need either to get rid of trailing slashes and utilize 301 Moved Permanently in prod; or live with broken trailing slash support in dev.

To Reproduce

Here is minimal reproducible case (link to repro repo is below snippet):

// pages/index.js
import Link from "next/link";

export default () => (
  <Link href="/about/">
    <a>About</a>
  </Link>
);

// pages/index.js
export default () => "about";

Minimal reproducible repo https://github.com/iamstarkov/next.js-trailing-slash-bug-demo

  1. clone repo git clone https://github.com/iamstarkov/next.js-trailing-slash-bug-demo
  2. change directory cd next.js-trailing-slash-bug-demo
  3. install deps yarn
  4. run dev: yarn dev
  5. open http://localhost:3000/
  6. open devtools' network tab
  7. observe http://localhost:3000/_next/static/development/pages/about.js being 200ed
  8. observe http://localhost:3000/_next/on-demand-entries-ping?page=/about/ being 200ed
  9. observe http://localhost:3000/about/ being 404ed
  10. observe persistent attempts to resolve http://localhost:3000/about/
  11. observe in the terminal Client pings, but there's no entry for page: /about/
  12. refresh the page
  13. observe 404 page.
  14. remove trailing slash in the url or click http://localhost:3000/about
  15. observe page being 200ed
  16. to ensure error persistence repeat steps 5-15 once.

Expected behavior

  1. /about/ shouldnt be resolved as 404 not found
  2. /about/ should be resolved as 200 ok
  3. Server should not print Client pings, but there's no entry for page: /about/
  4. both /about and /about/ should work the same way

Screenshots

N/A

System information

Additional context

Add any other context about the problem here.

If you change this code in https://github.com/zeit/next.js/blob/459c1c13d054b37442126889077b7056269eeb35/server/on-demand-entry-handler.js#L242-L249

or node_modules/next/dist/server/on-demand-entry-handler.js locally

          const { query } = parse(req.url, true)
          const page = normalizePage(query.page)
+         console.log('query.page', query.page);
+         console.log('page', page);
+         console.log('Object.keys(entries)', Object.keys(entries));
          const entryInfo = entries[page]

          // If there's no entry.
          // Then it seems like an weird issue.
          if (!entryInfo) {
            const message = `Client pings, but there's no entry for page: ${page}`

and restart next dev and open http://localhost:3000/ and click about link then:

I think the problem (at least part of it) is in inability of onDemandEntryHandler's middleware to find page in entries if page has trailing slash.

I hope my 2 hours of investigation and preparation can help with fixing this issue.

AlexSapoznikov commented 4 years ago

The problem with implementing a workaround in Error component is that it will throw notifying error in development which bothers me. Some improvement to my previous client side redirect:

What improved is that now it uses next/router in client side and the url replacement happens without a reload.

pages/_app.jsx

import App from 'next/app';
import Router from 'next/router';

export default class MyApp extends App {
  render() {
    const { Component, pageProps, router: { asPath, route } } = this.props;

    // Next.js currently does not allow trailing slash in a route.
    // This is a client side redirect in case trailing slash occurs.
    if (pageProps.statusCode === 404 && asPath.length > 1 && asPath.endsWith('/')) {
      const routeWithoutEndingSlash = route.replace(/\/*$/gim, '');
      const asPathWithoutEndingSlash = asPath.replace(/\/*$/gim, '');

      if (typeof window !== 'undefined') {
        Router.replace(routeWithoutEndingSlash, asPathWithoutEndingSlash);
      }
      return null;
    }

    return <Component {...pageProps} />;
  }
}

also thanks to @mbrowne for 404 fix :)

pinpointcoder commented 4 years ago

Took @cansin 's solution and added the ability to handle query parameters

MyError.getInitialProps = async ({ res, err, asPath }) => {
  // Capture 404 of pages with traling slash and redirect them
  const statusCode = res 
    ? res.statusCode
    : (err ? err.statusCode : 404);

  if (statusCode && statusCode === 404) {
    const [path, query = ''] = asPath.split('?');                                                                                                                                                                                             
    if (path.match(/\/$/)) {
      const withoutTrailingSlash = path.substr(0, path.length - 1); 
      if (res) {
        res.writeHead(302, {
          Location: `${withoutTrailingSlash}${query ? `?${query}` : ''}`,
        }); 
        res.end();
      } else {
        Router.push(`${withoutTrailingSlash}${query ? `?${query}` : ''}`);
      }   
    }   
  }
coodoo commented 4 years ago

@pinpointcoder can you provide examples of a url with trailing slash and query parameters happen at the same time? Are you thinking along the line of /blog/?123?

haotangio commented 4 years ago

Thanks everyone for some of your workarounds above. They worked!

However, do we have any official way to fix this issue from Next's team? This issue has been here for years.

vikrantraut commented 4 years ago

Directory pages are not served with trailing slash in next export

pjaws commented 4 years ago

@pinpointcoder can you provide examples of a url with trailing slash and query parameters happen at the same time? Are you thinking along the line of /blog/?123?

@coodoo Not him, but yes, unfortunately this happens a lot. I'm currently in the process of incrementally migrating a WordPress site onto Next.js, and for some reason, the original "developers" decided to force a trailing slash on every single URL, so we currently have tons of requests with both a trailing slash AND query parameters.

As we're about to migrate tons of blog posts for which the canonical URL currently includes a trailing slash, this is a giant pain in my ass right now.

mbrowne commented 4 years ago

I decided to implement a custom server to handle this and it turns out it's easy to do, and you can still use next.js's file-based routing system. That way you can rewrite the URL that next.js sees and the real URL still has a slash at the end:

const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const conf = require('./next.config.js')

const PORT = process.env.PORT || 5000

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev, conf })
const handle = app.getRequestHandler()

app.prepare().then(() => {
    createServer((req, res) => {
        // If there is a slash at the end of the URL, remove it before sending it to the handle() function.
        // This is a workaround for https://github.com/zeit/next.js/issues/5214
        const url =
            req.url !== '/' && req.url.endsWith('/')
                ? req.url.slice(0, -1)
                : req.url
        // Be sure to pass `true` as the second argument to `url.parse`.
        // This tells it to parse the query portion of the URL.
        const parsedUrl = parse(url, true)

        handle(req, res, parsedUrl)
    }).listen(PORT, err => {
        if (err) throw err
        console.log(`> Ready on http://localhost:${PORT}`)
    })
})

See https://nextjs.org/docs/advanced-features/custom-server

pjaws commented 4 years ago

@mbrowne We actually have a bunch of reasons to use a custom server, but the main thing that has prevented me from implementing one so far is the fact that you lose Automatic Static Optimization. Do you know if it's possible to manually specify static routes?

mbrowne commented 4 years ago

We don't need automatic static optimization for our app at the moment, so I haven't looked into it.

untitledlt commented 4 years ago

I am also using custom server but when you pass modified (without leading slash) url to handle, SSR sees different url from client side. I would prefer next router to match url with leading slash without those nasty hacks.

jhvissotto commented 4 years ago

2020 and this bug still hapens. Unbelievable

chaddjohnson commented 4 years ago

This is a bad bug that really needs to be fixed. /products works, but /products/ doesn't. With this link

<Link href="/products">
  <a>Products</a>
</Link>

I get

index.js:1 Warning: Prop `href` did not match. Server: "/products" Client: "/products/"

However, if I point the link to /products/, visit the link, and refresh the page during development, I get a 404. This is quite a painful development experience.

This issue was first reported 1.5 years ago; can we please get an official fix? It's still present in 9.3.4.

acro5piano commented 4 years ago

I made redirection to non-trailing slash url instead of showing contents, for SEO reason.

app.prepare().then(() => {
  createServer((req, res) => {
    if (req.url !== '/' && req.url.endsWith('/')) {
      res.writeHead(301, { Location: req.url.slice(0, -1) })
      res.end()
    }
    handle(req, res, parse(req.url, true))
  }).listen(PORT, err => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${PORT}`)
  })
})
kenberkeley commented 4 years ago

For SEO, rel="canonical" may help, but still need to fix this 404 issue.

JoeHart commented 4 years ago

This is a bad bug that really needs to be fixed. /products works, but /products/ doesn't. With this link

<Link href="/products">
  <a>Products</a>
</Link>

I get

index.js:1 Warning: Prop `href` did not match. Server: "/products" Client: "/products/"

However, if I point the link to /products/, visit the link, and refresh the page during development, I get a 404. This is quite a painful development experience.

This issue was first reported 1.5 years ago; can we please get an official fix? It's still present in 9.3.4.

I am also currently getting this issue.

thisisayush commented 4 years ago

Here's how I fixed it, https://medium.com/@thisisayush/handling-404-trailing-slash-error-in-nextjs-f8844545afe3

chaddjohnson commented 4 years ago

Here's how I fixed it, https://medium.com/@thisisayush/handling-404-trailing-slash-error-in-nextjs-f8844545afe3

Thank you, though this requires a custom server when developing locally, and one shouldn't be required.

@timneutkens Any chance a fix for this issue can be worked into the development schedule?

mbrowne commented 4 years ago

More importantly, the redirect solution doesn't work for those who are maintaining sites that area already set up to add a slash rather than remove one in production. I don't think the framework should be dictating this choice arbitrarily.

philipstanislaus commented 4 years ago

@AlexSapoznikov 's solution worked well for us with Netlify (which adds a trailing slash by default). Here is an advanced version that adds support for query params:

import App from "next/app";

export default class MyApp extends App {
  render() {
    const { Component, pageProps, router, router: { asPath } } = this.props;

    // Next.js currently does not allow trailing slash in a route, but Netlify appends trailing slashes. This is a
    // client side redirect in case trailing slash occurs. See https://github.com/zeit/next.js/issues/5214 for details
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/")) {
        const asPathWithoutTrailingSlash = path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (typeof window !== "undefined") {
          router.replace(asPathWithoutTrailingSlash, undefined, { shallow: true });
          return null;
        }
      }
    }

    return <Component {...pageProps} />;
  }
}
ronyeh commented 4 years ago

I apologize because I'm a Next JS newbie, although I have software development experience on other SDKs and platforms.

I think this "bug" surprised me the most. For me, it violated the "principle of least astonishment." I simply expected my /about/ and /about to work the same, since I placed an index.tsx into my /pages/about/ folder.

I first started making web sites in the late 1990s with HTML FTP'd to my server, and later moved on to PHP & Apache, and eventually Java servers. Now I specialize in mobile apps. It just feels weird to me that this behavior is not the default, and that I'd have to write a custom server page to fix it on my dev server.

I plan to do a static export, so it won't show up in production even if I don't write the custom server. It does make dev and debugging slightly more annoying though.

Can we get a "next dev" flag that fixes this so we lazy developers don't need to write extra routing logic just for dev/debug time?

Thanks!

p.s.: Yes, I do know that /about and /about/ are completely different URLs. I just got really confused when I put an index.tsx file inside my /pages/about/ folder, and discovered that it only works with the /about path but does not work with /about/. I would be less surprised if it was the other way around.

p.p.s.: It was extra confusing when I have a <Link></Link> component that points to /about/ and it works as expected. Then when I hit refresh on my browser, it immediately 404s, even though the URL didn't change. That was very surprising. :-D

pjaws commented 4 years ago

But wait, it gets worse! We added a custom checkForTrailingSlash function inside _error.js that would strip the trailing slash and redirect. This worked okay for a while until we (finally) added a custom 404 page and found that with a custom 404 page, Next.js completely bypasses Error. This means none of your custom logic inside Error.getInitialProps will work anymore - including a check for trailing slashes.

Guess I'll try the _app.js solution others mentioned, as a custom server is just not a possibility quite yet.

pjaws commented 4 years ago

@AlexSapoznikov 's solution worked well for us with Netlify (which adds a trailing slash by default). Here is an advanced version that adds support for query params:

import App from "next/app";

export default class MyApp extends App {
  render() {
    const { Component, pageProps, router, router: { asPath } } = this.props;

    // Next.js currently does not allow trailing slash in a route, but Netlify appends trailing slashes. This is a
    // client side redirect in case trailing slash occurs. See https://github.com/zeit/next.js/issues/5214 for details
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/")) {
        const asPathWithoutTrailingSlash = path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (typeof window !== "undefined") {
          router.replace(asPathWithoutTrailingSlash, undefined, { shallow: true });
          return null;
        }
      }
    }

    return <Component {...pageProps} />;
  }
}

There is a critical error in your code sample: requests to the index route with a query parameter will throw an error, since you end up attempting to pass just the query string to Next.js as the asPath.

This fixes it:

  if (asPath && asPath.length > 1) {
    const [path, query = ''] = asPath.split('?');
    if (path.endsWith('/') && path.length > 1) {
      const asPathWithoutTrailingSlash =
        path.replace(/\/*$/gim, '') + (query ? `?${query}` : '');
      if (typeof window !== 'undefined') {
        router.replace(asPathWithoutTrailingSlash, undefined, {
          shallow: true,
        });
        return null;
      }
    }
  }
adonig commented 4 years ago

To make this work with SSR I had to add the following to the @pjaws & @AlexSapoznikov solution:

  static async getInitialProps({ Component, ctx, router }) {
    /* Fixes the trailing-slash-404 bug for server-side rendering. */
    const { asPath } = router;
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/") && path.length > 1) {
        const asPathWithoutTrailingSlash =
          path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (ctx.res) {
          ctx.res.writeHead(301, {
            Location: asPathWithoutTrailingSlash,
          });
          ctx.res.end();
        }
      }
    }
    return {
      pageProps: Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {},
    };
  }

Probably it's a good idea to somehow generalize this functionality into a function that works both during SSR and during CSR and call it in both places (getInitialProps and render).

IRediTOTO commented 4 years ago

by

this will fix but the tittle wrong. Hmm image

Izhaki commented 4 years ago

@AlexSapoznikov @pjaws

Your solution puts us in infinite loop:

  if (asPath && asPath.length > 1) {
    const [path, query = ''] = asPath.split('?');
    if (path.endsWith('/') && path.length > 1) {
      const asPathWithoutTrailingSlash =
        path.replace(/\/*$/gim, '') + (query ? `?${query}` : '');
      if (typeof window !== 'undefined') {
        router.replace(asPathWithoutTrailingSlash, undefined, {
          shallow: true,
        });
        return null;
      }
    }
  }

Context

Due to reasons beyond our control, we have to use the exportTrailingSlash option in next.config.js.

We want to have a link to another page but we want the link to be /somepage?param=whatever.

It seems that next link converts this to /somepage/?param=whatever and we get page not found.

Using the solution above solve the params problem, but then when going to a deployed page like /somepage/ it enters an infinite loop.

nathantqn commented 4 years ago

I think @ronyeh had made a really good point here, so I really want an official solution for this issue :(

gauravkrp commented 4 years ago

To make this work with SSR I had to add the following to the @pjaws & @AlexSapoznikov solution:

  static async getInitialProps({ Component, ctx, router }) {
    /* Fixes the trailing-slash-404 bug for server-side rendering. */
    const { asPath } = router;
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/") && path.length > 1) {
        const asPathWithoutTrailingSlash =
          path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (ctx.res) {
          ctx.res.writeHead(301, {
            Location: asPathWithoutTrailingSlash,
          });
          ctx.res.end();
        }
      }
    }
    return {
      pageProps: Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {},
    };
  }

Probably it's a good idea to somehow generalize this functionality into a function that works both during SSR and during CSR and call it in both places (getInitialProps and render).

This has worked for pages with getServerSideProps and now urls with trailing slashes are returning same page without 404. But there is one glitch, I have few pages that use dynamic routes and getStaticPaths, I can't use getServerSideProps onto them and thus when these dynamic routes are browsed with a trailing slash, they first return a 404 and then they redirect to the page.

andrescabana86 commented 4 years ago

I am working with an /api/test folder

it works for

and I just discovered that this doesn't work

not sure if this is related issue P/D exportTrailingSlash = true does not solve it

Valnexus commented 4 years ago

This is a very old issue, is there a reason it is not addressed for so long?

Izhaki commented 4 years ago

I'm not sure what is not working anymore.

My understating is that the requirements are as follows:

exportTrailingSlash: false exportTrailingSlash: true
url ends with / Shouldn't work Should work
url does not end with / Should work Shouldn't work

This works as expected where:

From what I can see in @andrescabana86 This works where it shouldn't: GET /api/test/123/ whereas GET /api/test/ doesn't work and it shouldn't.

andrescabana86 commented 4 years ago

@Izhaki I tried both, deploying on prod... and for me is not working

and I am using exportTrailingSlash: true

I can try creating a public repo if you want, maybe I forgot something in the middle.

thank you for your answers

Izhaki commented 4 years ago

@andrescabana86 I'm not sure how much a public repo will help here - this may well be some configuration on the server you deploy onto.

We are testing our production builds (with exportTrailingSlash: true) locally using this script in package.json:

"serve:out": "docker run --rm -v $(pwd)/out:/static -p 5000:80 flashspys/nginx-static"

Please let me know if going in your browser to http://localhost:5000/api/test/ works.

(Note that $(pwd) is on Mac/Linux - see this for windows)

cansin commented 4 years ago

@Izhaki the problem was about the fact that (as the initial report suggests) "trailing slash in link for legit page works for client side navigation but leads to not found bundle and 404 on hard refresh (ssr)". So there was a mismatch between the behavior of a client-side route change, versus a hard refresh. I am not sure if the problem persists with the latest version of Next.js. I can report back here once I test it.

Izhaki commented 4 years ago

Just tested with 9.4.1 and exportTrailingSlash: true.

Going to http://localhost:6500/admin/ returns 404 when developing locally.

But the same path works when you export.

Note that exportTrailingSlash hints this is for exports only.

What we do is use:

exportTrailingSlash: process.env.NODE_ENV === 'production'

That means things work as intended when we develop locally. And work properly when deployed (via export).

Isn't that the correct and viable solution for this?

cansin commented 4 years ago

If a URL does not work on development but does work on production, don't you think that is against the principle of least surprise? I think this should still be considered a bug.

^ That said, I am pretty sure previously on production there was a conflicting behavior between a page refresh vs a router.push event. I don't know if it is still the case.

chaddjohnson commented 4 years ago

@andrescabana86 @Izhaki exportTrailingSlash is unrelated to this. That option relates to static exporting of Next.js applications. When true, example/index.html is generated, whereas when it is false, example.html is generated. My understanding is that exportTrailingSlash has nothing to do with development mode.

Izhaki commented 4 years ago

I think one source of confusion is that when you have exportTrailingSlash next.js adds a trailing slash to links. This happens in development as well I'm not sure it should do this? But anyhow, this is not only about example/index.html vs example.html - you need links to be modified as well.

If a URL does not work on development but does work on production, don't you think that is against the principle of least surprise? I think this should still be considered a bug.

I may be wrong, but exportTrailingSlash option was for nginx servers that are not configured to serve /something.html when the url is /something.

This is not the case with the next server used for local dev. So what works and what doesn't depends on what serves your app.

You can make a case that when exportTrailingSlash is true, the next server should support routes ending with a trailing slash (although this will make the export in exportTrailingSlash somewhat irrelevant).

timneutkens commented 4 years ago

FWIW this is being worked on already #13333

Spirik commented 4 years ago

I am not very experienced coder, using Next.js primarily for multi-paged landings. Apparently, I've been using the following workaround almost all of the time, unbeknownst to its effect. Here's stripped down version of it:

// In your server.js
server.get('/:id', (req, res) => {
  const actualPage = `/${req.params.id}`
  app.render(req, res, actualPage)
})

In my case the code is a little more complicated, because I am using it to support additional static url prefixes, etc. But this stripped down version seems to be working for the discussed issue just fine, regardless of the exportTrailingSlash setting and its effect on Links. E.g. URLs /about and /about/ work just fine.

In present form It essentially mimics the native routing of Next.js. The downside: it requires custom server.js, and you will have to manually support it for "deeper" URLs (with additional "subfolders"), e.g. /company/about/. But it seems to be relatively simple solution for those who already use custom server.js in their project.

pjaws commented 4 years ago

To make this work with SSR I had to add the following to the @pjaws & @AlexSapoznikov solution:

  static async getInitialProps({ Component, ctx, router }) {
    /* Fixes the trailing-slash-404 bug for server-side rendering. */
    const { asPath } = router;
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/") && path.length > 1) {
        const asPathWithoutTrailingSlash =
          path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (ctx.res) {
          ctx.res.writeHead(301, {
            Location: asPathWithoutTrailingSlash,
          });
          ctx.res.end();
        }
      }
    }
    return {
      pageProps: Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {},
    };
  }

Probably it's a good idea to somehow generalize this functionality into a function that works both during SSR and during CSR and call it in both places (getInitialProps and render).

This has worked for pages with getServerSideProps and now urls with trailing slashes are returning same page without 404. But there is one glitch, I have few pages that use dynamic routes and getStaticPaths, I can't use getServerSideProps onto them and thus when these dynamic routes are browsed with a trailing slash, they first return a 404 and then they redirect to the page.

@gauravkrp This is actually an extremely important addition, since @AlexSapoznikov solution will actually still return a 404 for the page to Google (since the redirect happens on the client). I imagine SEO is a major reason a lot of us are using Next.js in the first place.

I also think that putting this in getInitialProps should just work all around, and the piece inside the main function is unnecessary at this point. The major caveat here is that you are losing Automatic Static Optimization by having this - probably better than a bunch of 404s, though.

Cow258 commented 4 years ago

For some sharing...

My project is Express + Next.js.\ express 4.17.1\ next 9.4.5-canary.7

When development

Dynamic Runtime

// next.config.js
module.exports = {
  exportTrailingSlash: false,
};

// app.js
const Next = require('next').default;
const NextApp = Next({ dev });
const NextHandler = NextApp.getRequestHandler();
NextApp.prepare();
app.get('*', (req, res) => NextHandler(req, res));

When Production

Static Export Run next build and next export -o dist/

// next.config.js
module.exports = {
  exportTrailingSlash: true,
};

// app.js
app.use('/_next', express.static('dist/_next', { etag: true, index: false, maxAge: '365d', redirect: false, dotfiles: 'ignore' }));
app.use('/fonts', express.static('dist/fonts', { etag: true, index: false, maxAge: '365d', redirect: false, dotfiles: 'ignore' }));
app.use('/img', express.static('dist/img', { etag: true, index: false, maxAge: '365d', redirect: false, dotfiles: 'ignore' }));
app.use(express.static('./dist', { index: ['index.html'] }));
app.use((req, res) => {
  res.Redirect('/404'); // <- Express will auto handle both /404 or /404/
});

In Conclusion

I have no issue when redirect by clicking on client app,\ also hard refresh is working on static route.

But it will 404 when hard refresh on dynamic route,\ like /album/[id].jsx or /album/123,\ So I'm looking forward to fix this issue by using the following mechanism.

e.g.\ When hit 404 at /album/123,\ server should continue to provide html content,\ browser will continue to load the page without issue,\ when Next.js boot up then next/router should auto handle it.

ipaddr commented 4 years ago

is there any temporary solution to this issue on production?

Timer commented 4 years ago

We're about to land a feature fixing this—a day or so!

pjaws commented 4 years ago

is there any temporary solution to this issue on production?

There are many in this thread, but I'm currently using what @gauravkrp posted recently, and it's working well for me.

timneutkens commented 4 years ago

You can keep track of the PR here: #13333

Timer commented 4 years ago

This has now been resolved in next@^9.4.5-canary.17!

untitledlt commented 4 years ago

How long does it take for feature to get from canary to master?

Valnexus commented 4 years ago

This has now been resolved in next@^9.4.5-canary.17!

And how exactly it is resolved? just removing the trailing slash? if i access "www.site.com/help/" i get redirected to: "www.site.com/help" , can we have an option there we opt for leaving ending slash? accessing "www.site.com/help/" or "www.site.com/help" will leave or redirect or add "/" at the end to have: "www.site.com/help/"

timneutkens commented 4 years ago

@Valnexus see #13333, it includes an experimental option:

module.exports = {
  experimental: {
    trailingSlash: true
  }
}

How long does it take for feature to get from canary to master?

When it's ready. There are still edge cases in the handling that are being solved. Once those have been fixed it can go to stable.

armspkt commented 4 years ago

@timneutkens @Janpot

I tried the latest next canary (9.4.5-canary.27) but when I create test page and I access www.example/test/ it redirects to www.example/test I think behavior for both cases should be the same.

When access www.example/test/ it should stay on www.example/test/. When access www.example/test it should stay on www.example/test. I test it on Nuxt.js, it works the same behavior that I describe above.