vercel / next.js

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

asPath on the server does not match the requested URL (custom server) #55521

Open gudvinga opened 11 months ago

gudvinga commented 11 months ago

Link to the code that reproduces this issue or a replay of the bug

https://github.com/gudvinga/next-as-path-issue

To Reproduce

  1. Start application on development (yarn serve)
  2. Open http://localhost:3000/i-am-fake/as-path/page
  3. Get an error

Current vs. Expected behavior

Current:

When I go to the page http://localhost:3000/i-am-fake/as-path/page I get the error "Error: The text content does not match the HTML displayed by the server" because the asPath on the server is not match request URL (and doesn't match router.asPath from useRouter)

Unhandled Runtime Error
Error: Text content does not match server-rendered HTML.
Warning: Text content did not match. Server: "/" Client: "/i-am-fake/as-path/page"
See more info here: https://nextjs.org/docs/messages/react-hydration-error

Expected:

asPath from context in getInitialProps should return the same value as router.asPath from useRouter on the client.

Verify canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 22.6.0: Wed Jul  5 22:22:52 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T8103
    Binaries:
      Node: 16.18.0
      npm: 9.2.0
      Yarn: 1.22.19
      pnpm: N/A
    Relevant Packages:
      next: 13.4.20-canary.35
      eslint-config-next: N/A
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.2.2
    Next.js Config:
      output: N/A

Which area(s) are affected? (Select all that apply)

Data fetching (gS(S)P, getInitialProps)

Additional context

I tested my copy on different versions and the first one to introduce the bug was "13.4.13", since reverting to "13.4.12" works

LucaNerlich commented 11 months ago

Most likely same issue as here https://github.com/vercel/next.js/issues/54008

gudvinga commented 11 months ago

Most likely same issue as here https://github.com/vercel/next.js/issues/54008

Thanks, but as I understand, it's not the same. In my case, something happened with app.render method on the server

I think it might be related to this PR (https://github.com/vercel/next.js/pull/51939). But I can't find a bug in these changes.

felixmosh commented 11 months ago

Same here, reproduces in Next.js 13.5.2

gudvinga commented 11 months ago

I want to add some context: in new versions on Next.js we rewrite req.url here next.js/packages/next/src/server/next.ts

  async render(...args: Parameters<Server['render']>) {
    let [req, res, pathname, query, parsedUrl] = args
    this.setupWebSocketHandler(this.options.httpServer, req as any)

    if (!pathname.startsWith('/')) {
      console.error(`Cannot render page with path "${pathname}"`)
      pathname = `/${pathname}`
    }
    pathname = pathname === '/index' ? '/' : pathname

    req.url = formatUrl({
      ...parsedUrl,
      pathname,
      query,
    })

    await this.requestHandler(req as any, res as any)
    return
  }
}

But in base-server.ts we use req.url to define the urlPathname here next.js/packages/next/src/server/base-server.ts

        // For getServerSideProps and getInitialProps we need to ensure we use the original URL
        // and not the resolved URL to prevent a hydration mismatch on
        // asPath
        resolvedAsPath:
          hasServerProps || hasGetInitialProps
            ? formatUrl({
                // we use the original URL pathname less the _next/data prefix if
                // present
                pathname: `${urlPathname}${hadTrailingSlash ? '/' : ''}`,
                query: origQuery,
              })
            : resolvedUrl,

        supportsDynamicHTML,
        isOnDemandRevalidate,
        isDraftMode: isPreviewMode,
        isServerAction,
      }

As a solution, use some custom req property like _originalUrl to provide asPath to all functions.

gudvinga commented 11 months ago

@felixmosh As workaround in my project I moved the routing from the custom server to the Next.js middleware like here https://nextjs.org/docs/pages/api-reference/components/link#middleware

On the custom server I leave only await handle(req, res, parsedUrl); In the middleware I use return NextResponse.rewrite(new URL('/correctUrl', req.url))

But there is a limitation: Next.js middleware only supports Edge runtime =(

felixmosh commented 11 months ago

Yeah, it not suitable for me, thanx anyway.

This is clearly a bug, since v13.4 is working properly, will wait till the team fixes it.

adam187 commented 11 months ago

I got it working as before with help of https://github.com/ds300/patch-package

next+13.5.3.patch

jomarl commented 10 months ago

The change in behavior locks us to 13.4 until it's fixed - or we will eventually have to start looking at workarounds. We're using the pages router and I guess that is no longer a priority.

dan-b-hu commented 9 months ago

Specifically it seems to be broken in 13.4.13, so we're stuck on 13.4.12 until it's fixed or we migrate our ~50 route to app router...

Edit: OP already said which version is broken

jomarl commented 3 months ago

Specifically it seems to be broken in 13.4.13, so we're stuck on 13.4.12 until it's fixed or we migrate our ~50 route to app router...

Edit: OP already said which version is broken

Migrating to the app router is not an option for us. Seems like we have to start looking at the work arounds as Vercel never seem to fix bugs but just push new features. This is still broken in 14.2.3.

This actually broke on a patch version. Are not patch versions supposed to be 100% backward compatible?

I know I'm probably not 100% fair here, but it's a bit frustrating that router.asPath broke on a minor version and it's been more than half a year since it was reported. It's not good that breaking bugs like this is not prioritized as the "new" inconsistent behavior might be what people now expects and hence it could be problematic to actually fix it without breaking the functionallity for those who started out on version 13.4.3 or later.

Main concern is if a serious security issues come up and we are blocked from upgrading.