Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix next-server not working properly when HOSTNAME is defined #54926

Closed
wants to merge 14 commits into from
3 changes: 2 additions & 1 deletion docs/02-app/02-api-reference/05-next-config-js/output.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ Additionally, a minimal `server.js` file is also output which can be used instea

> **Good to know**:
>
> - If your project needs to listen to a specific port or hostname, you can define `PORT` or `HOSTNAME` environment variables before running `server.js`. For example, run `PORT=8080 HOSTNAME=0.0.0.0 node server.js` to start the server on `http://0.0.0.0:8080`.
> - If your project uses [Image Optimization](/docs/app/building-your-application/optimizing/images) with the default `loader`, you must install `sharp` as a dependency:
> - If your project needs alternative port or hostname for listening, you can define `PORT` and `HOSTNAME` environment variables, before running `server.js`. For example, `PORT=3000 HOSTNAME=localhost node server.js`.

</AppOnly>

Expand All @@ -49,6 +49,7 @@ Additionally, a minimal `server.js` file is also output which can be used instea
> **Good to know**:
>
> - `next.config.js` is read during `next build` and serialized into the `server.js` output file. If the legacy [`serverRuntimeConfig` or `publicRuntimeConfig` options](/docs/pages/api-reference/next-config-js/runtime-configuration) are being used, the values will be specific to values at build time.
> - If your project needs to listen to a specific port or hostname, you can define `PORT` or `HOSTNAME` environment variables before running `server.js`. For example, run `PORT=8080 HOSTNAME=0.0.0.0 node server.js` to start the server on `http://0.0.0.0:8080`.
> - If your project uses [Image Optimization](/docs/pages/building-your-application/optimizing/images) with the default `loader`, you must install `sharp` as a dependency:

</PagesOnly>
Expand Down
4 changes: 3 additions & 1 deletion examples/with-docker-multi-env/docker/development/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ USER nextjs
EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME localhost
# Uncomment the following line if `process.env.HOSTNAME` is not provided by your provider
# and `localhost` doesn't work with your use case.
# ENV HOSTNAME 0.0.0.0

CMD ["node", "server.js"]
4 changes: 3 additions & 1 deletion examples/with-docker-multi-env/docker/production/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ USER nextjs
EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME localhost
# Uncomment the following line if `process.env.HOSTNAME` is not provided by your provider
# and `localhost` doesn't work with your use case.
# ENV HOSTNAME 0.0.0.0

CMD ["node", "server.js"]
4 changes: 3 additions & 1 deletion examples/with-docker-multi-env/docker/staging/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ USER nextjs
EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME localhost
# Uncomment the following line if `process.env.HOSTNAME` is not provided by your provider
# and `localhost` doesn't work with your use case.
# ENV HOSTNAME 0.0.0.0

CMD ["node", "server.js"]
5 changes: 3 additions & 2 deletions examples/with-docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ USER nextjs
EXPOSE 3000

ENV PORT 3000
# set hostname to localhost
ENV HOSTNAME "0.0.0.0"
# Uncomment the following line if `process.env.HOSTNAME` is not provided by your provider
# and `localhost` doesn't work with your use case.
# ENV HOSTNAME 0.0.0.0

CMD ["node", "server.js"]
4 changes: 2 additions & 2 deletions packages/next/src/experimental/testmode/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import type {
ProxyResponse,
} from './proxy'
import { ClientRequestInterceptor } from 'next/dist/compiled/@mswjs/interceptors/ClientRequest'
import { WorkerRequestHandler } from '../../server/lib/setup-server-worker'
import { NodeRequestHandler } from '../../server/next-server'
import type { WorkerRequestHandler } from '../../server/lib/setup-server-worker'
import type { NodeRequestHandler } from '../../server/next-server'

interface TestReqInfo {
url: string
Expand Down
28 changes: 11 additions & 17 deletions packages/next/src/lib/turbopack-warning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,11 @@ export async function validateTurboNextConfig({

let thankYouMessage =
[
`Thank you for trying Next.js v13 with Turbopack! As a reminder`,
`Turbopack is currently in beta and not yet ready for production.`,
`We appreciate your ongoing support as we work to make it ready`,
`for everyone.`,
]
.map((line) => `${line}`)
.join('\n') + '\n\n'
'Thank you for trying Next.js v13 with Turbopack! As a reminder',
'Turbopack is currently in beta and not yet ready for production.',
'We appreciate your ongoing support as we work to make it ready',
'for everyone.',
].join('\n') + '\n\n'

let unsupportedParts = ''
let babelrc = await getBabelConfigFile(dir)
Expand Down Expand Up @@ -205,36 +203,32 @@ export async function validateTurboNextConfig({
`\n${chalk.yellow(
'Warning:'
)} Webpack is configured while Turbopack is not, which may cause problems.\n
${`See instructions if you need to configure Turbopack:\n https://turbo.build/pack/docs/features/customizing-turbopack\n`}`
See instructions if you need to configure Turbopack:\n https://turbo.build/pack/docs/features/customizing-turbopack\n`
)
}

if (babelrc) {
unsupportedParts += `\n- Babel detected (${chalk.cyan(
babelrc
)})\n ${`Babel is not yet supported. To use Turbopack at the moment,\n you'll need to remove your usage of Babel.`}`
)})\n Babel is not yet supported. To use Turbopack at the moment,\n you'll need to remove your usage of Babel.`
}
if (unsupportedConfig.length) {
unsupportedParts += `\n\n- Unsupported Next.js configuration option(s) (${chalk.cyan(
'next.config.js'
)})\n ${`To use Turbopack, remove the following configuration options:\n${unsupportedConfig
)})\n To use Turbopack, remove the following configuration options:\n${unsupportedConfig
.map((name) => ` - ${chalk.red(name)}\n`)
.join('')} `} `
.join('')}`
}

if (unsupportedParts && !isCustomTurbopack) {
const pkgManager = getPkgManager(dir)

console.error(
`${'Error:'} You are using configuration and/or tools that are not yet\nsupported by Next.js v13 with Turbopack:\n${unsupportedParts}\n
`Error: You are using configuration and/or tools that are not yet\nsupported by Next.js v13 with Turbopack:\n${unsupportedParts}\n
If you cannot make the changes above, but still want to try out\nNext.js v13 with Turbopack, create the Next.js v13 playground app\nby running the following commands:

${chalk.bold.cyan(
`${
pkgManager === 'npm'
? 'npx create-next-app'
: `${pkgManager} create next-app`
} --example with-turbopack with-turbopack-app`
`${pkgManager} create next-app --example with-turbopack with-turbopack-app`
)}\n cd with-turbopack-app\n ${pkgManager} run dev
`
)
Expand Down
1 change: 0 additions & 1 deletion packages/next/src/server/lib/format-hostname.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { isIPv6 } from './is-ipv6'
* @param hostname
* @returns
*/

export function formatHostname(hostname: string): string {
return isIPv6(hostname) ? `[${hostname}]` : hostname
}
19 changes: 19 additions & 0 deletions packages/next/src/server/lib/get-fetch-hostname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { formatHostname } from './format-hostname'

/**
* Gets the hostname used to fetch pages (also checks whether the server
* is running on `0.0.0.0` or `::`).
* @param actualHostname - The host the server runs on.
* @param providedHostname - The hostname the user provides.
* @returns
*/
export function getFetchHostname(
actualHostname: string,
providedHostname: string | undefined
) {
return !providedHostname || actualHostname === '0.0.0.0'
? 'localhost'
: actualHostname === '::'
? '[::1]'
: formatHostname(providedHostname)
}
16 changes: 10 additions & 6 deletions packages/next/src/server/lib/render-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import type { RequestHandler } from '../next'

// this must come first as it includes require hooks
import { initializeServerWorker } from './setup-server-worker'
import { formatHostname } from './format-hostname'
import next from '../next'
import { PropagateToWorkersField } from './router-utils/types'
import { getFetchHostname } from './get-fetch-hostname'

export const WORKER_SELF_EXIT_CODE = 77

Expand Down Expand Up @@ -88,25 +88,29 @@ export async function initialize(opts: {
let requestHandler: RequestHandler
let upgradeHandler: any

const providedHost = opts.hostname || 'localhost'
const serverWorkerOpts = {
...opts,
hostname: providedHost,
}

const { port, server, hostname } = await initializeServerWorker(
(...args) => {
return requestHandler(...args)
},
(...args) => {
return upgradeHandler(...args)
},
opts
serverWorkerOpts
)

app = next({
...opts,
_routerWorker: opts.workerType === 'router',
_renderWorker: opts.workerType === 'render',
hostname,
hostname: providedHost,
customServer: false,
httpServer: server,
port: opts.port,
isNodeDebugging: opts.isNodeDebugging,
})

requestHandler = app.getRequestHandler()
Expand All @@ -115,7 +119,7 @@ export async function initialize(opts: {

result = {
port,
hostname: formatHostname(hostname),
hostname: getFetchHostname(hostname, providedHost),
}

return result
Expand Down
5 changes: 2 additions & 3 deletions packages/next/src/server/lib/setup-server-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function initializeServerWorker(
port: number
dev: boolean
minimalMode?: boolean
hostname?: string
hostname: string
workerType: 'router' | 'render'
isNodeDebugging: boolean
keepAliveTimeout?: number
Expand Down Expand Up @@ -87,7 +87,6 @@ export async function initializeServerWorker(
upgradeHandler(req, socket, upgrade)
})
}
let hostname = opts.hostname || 'localhost'

server.on('listening', async () => {
try {
Expand Down Expand Up @@ -116,6 +115,6 @@ export async function initializeServerWorker(
return reject(err)
}
})
server.listen(0, hostname)
server.listen(0, opts.hostname)
})
}
26 changes: 10 additions & 16 deletions packages/next/src/server/lib/start-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import * as Log from '../../build/output/log'
import setupDebug from 'next/dist/compiled/debug'
import { getDebugPort } from './utils'
import { formatHostname } from './format-hostname'
import { getFetchHostname } from './get-fetch-hostname'
import { initialize } from './router-server'
import fs from 'fs'
import {
import type {
WorkerRequestHandler,
WorkerUpgradeHandler,
} from './setup-server-worker'
Expand All @@ -30,7 +31,7 @@ export interface StartServerOptions {
port: number
logReady?: boolean
isDev: boolean
hostname: string
hostname?: string
allowRetry?: boolean
customServer?: boolean
minimalMode?: boolean
Expand Down Expand Up @@ -59,7 +60,7 @@ export async function getRequestHandlers({
dir: string
port: number
isDev: boolean
hostname: string
hostname?: string
minimalMode?: boolean
isNodeDebugging?: boolean
keepAliveTimeout?: number
Expand Down Expand Up @@ -194,18 +195,15 @@ export async function startServer({
await new Promise<void>((resolve) => {
server.on('listening', async () => {
const addr = server.address()
const actualHostname = formatHostname(
const host =
typeof addr === 'object'
? addr?.address || hostname || 'localhost'
: addr
)

const formattedHostname =
!hostname || hostname === '0.0.0.0'
? 'localhost'
: actualHostname === '[::]'
? '[::1]'
: actualHostname
// The server's actual host
const formattedServerHost = formatHostname(host)
// appUrl's host - the server should respond when this hostname is fetched
const formattedHostname = getFetchHostname(host, hostname)

port = typeof addr === 'object' ? addr?.port || port : port
const appUrl = `${
Expand Down Expand Up @@ -233,11 +231,7 @@ export async function startServer({
)
Log.bootstrap(` - Local: ${appUrl}`)
if (hostname) {
Log.bootstrap(
` - Network: ${actualHostname}${
(port + '').startsWith(':') ? '' : ':'
}${port}`
)
Log.bootstrap(` - Network: ${formattedServerHost}:${port}`)
}
if (envInfo?.length)
Log.bootstrap(` - Environments: ${envInfo.join(', ')}`)
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1477,7 +1477,7 @@ export default class NextNodeServer extends BaseServer {
const locale = params.parsed.query.__nextLocale

url = `${getRequestMeta(params.request, '_protocol')}://${
this.fetchHostname
this.fetchHostname || 'localhost'
}:${this.port}${locale ? `/${locale}` : ''}${params.parsed.pathname}${
query ? `?${query}` : ''
}`
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/server/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { PHASE_PRODUCTION_SERVER } from '../shared/lib/constants'
import { getTracer } from './lib/trace/tracer'
import { NextServerSpan } from './lib/trace/constants'
import { formatUrl } from '../shared/lib/router/utils/format-url'
import {
import type {
WorkerRequestHandler,
WorkerUpgradeHandler,
} from './lib/setup-server-worker'
Expand Down
Loading