Repository: vercel/next.js
Pull request: Ensure custom NextServer config is honored (#81681)

This ensures if using NextServer or a custom-server directly we honor the custom config passed in correctly.

Backports to next-15-4 branch

Changed files:
filename: packages/next/src/server/next-server.ts
patch: @@ -1106,12 +1106,17 @@ export default class NextNodeServer extends BaseServer<
       routerServerGlobal[RouterServerContextSymbol] = {}
     }
     const relativeProjectDir = relative(process.cwd(), this.dir)
+    const existingServerContext =
+      routerServerGlobal[RouterServerContextSymbol][relativeProjectDir]
 
-    if (!routerServerGlobal[RouterServerContextSymbol][relativeProjectDir]) {
+    if (!existingServerContext) {
       routerServerGlobal[RouterServerContextSymbol][relativeProjectDir] = {
         render404: this.render404.bind(this),
       }
     }
+    routerServerGlobal[RouterServerContextSymbol][
+      relativeProjectDir
+    ].nextConfig = this.nextConfig
 
     try {
       // next.js core assumes page path without trailing slash

filename: test/production/custom-server/custom-server.test.ts
patch: @@ -50,6 +50,52 @@ describe('custom server', () => {
   })
 })
 
+describe('custom server provided config', () => {
+  const { next } = nextTestSetup({
+    files: __dirname,
+    startCommand: 'node server.js',
+    serverReadyPattern: /^- Local:/,
+    dependencies: {
+      'get-port': '5.1.1',
+    },
+    env: {
+      PROVIDED_CONFIG: 'true',
+    },
+  })
+
+  it.each(['a', 'b', 'c'])('can navigate to /%s', async (page) => {
+    const $ = await next.render$(`/docs/${page}`)
+    expect($('p').text()).toBe(`Page ${page}`)
+  })
+
+  describe('with app dir', () => {
+    it('should render app with react canary', async () => {
+      const $ = await next.render$(`/docs/1`)
+      expect($('body').text()).toMatch(/app: .+-canary/)
+    })
+
+    it('should render pages with installed react', async () => {
+      const $ = await next.render$(`/docs/2`)
+      if (isReact18) {
+        expect($('body').text()).toMatch(/pages: 18\.\d+\.\d+\{/)
+      } else {
+        expect($('body').text()).toMatch(/pages: 19\.\d+\.\d+/)
+      }
+    })
+
+    describe('when using "use cache" with a custom cache handler', () => {
+      it("should not unset the custom server's ALS context", async () => {
+        const cliOutputLength = next.cliOutput.length
+        const $ = await next.render$('/docs/use-cache')
+        expect($('p').text()).toBe('inner cache')
+        const cliOutput = next.cliOutput.slice(cliOutputLength)
+        expect(cliOutput).toMatch(createCacheSetLogRegExp('outer'))
+        expect(cliOutput).toMatch(createCacheSetLogRegExp('inner'))
+      })
+    })
+  })
+})
+
 describe('custom server with quiet setting', () => {
   const { next } = nextTestSetup({
     files: __dirname,

filename: test/production/custom-server/server.js
patch: @@ -10,8 +10,15 @@ let requestId = 0
 async function main() {
   const port = await getPort()
   const hostname = 'localhost'
+  let conf = undefined
+
+  if (process.env.PROVIDED_CONFIG) {
+    conf = require('./.next/required-server-files.json').config
+    conf.basePath = '/docs'
+  }
+
   // when using middleware `hostname` and `port` must be provided below
-  const app = next({ hostname, port, quiet })
+  const app = next({ hostname, port, quiet, conf })
   const handle = app.getRequestHandler()
 
   app.prepare().then(() => {
@@ -21,7 +28,11 @@ async function main() {
           // 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(req.url, true)
-          const { pathname, query } = parsedUrl
+          let { pathname, query } = parsedUrl
+
+          if (conf?.basePath) {
+            pathname = pathname.replace(conf.basePath, '') || '/'
+          }
 
           if (pathname === '/a') {
             await app.render(req, res, '/a', query)
@@ -30,6 +41,7 @@ async function main() {
           } else if (pathname === '/error') {
             await app.render(req, res, '/page-error')
           } else {
+            parsedUrl.pathname = pathname
             await handle(req, res, parsedUrl)
           }
         } catch (err) {