diff --git a/docs/config/index.md b/docs/config/index.md index 98fd6234fd480a..10716e71ef3736 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -328,8 +328,12 @@ export default async ({ command, mode }) => { ### server.host - **Type:** `string` +- **Default:** `'127.0.0.1'` - Specify server hostname. + Specify which IP addresses the server should listen on. + Set this to `0.0.0.0` to listen on all addresses, including LAN and public addresses. + + This can be set via the CLI using `--host 0.0.0.0` or `--host`. ### server.port diff --git a/packages/vite/src/node/cli.ts b/packages/vite/src/node/cli.ts index 41ed363388d7a7..2432488ad215a9 100644 --- a/packages/vite/src/node/cli.ts +++ b/packages/vite/src/node/cli.ts @@ -63,7 +63,7 @@ cli cli .command('[root]') // default command .alias('serve') - .option('--host ', `[string] specify hostname`) + .option('--host [host]', `[string] specify hostname`) .option('--port ', `[number] specify port`) .option('--https', `[boolean] use TLS + HTTP/2`) .option('--open [path]', `[boolean | string] open browser on startup`) diff --git a/packages/vite/src/node/preview.ts b/packages/vite/src/node/preview.ts index 2be9a40b73dec7..2574ca8211eace 100644 --- a/packages/vite/src/node/preview.ts +++ b/packages/vite/src/node/preview.ts @@ -41,33 +41,48 @@ export async function preview( ) const options = config.server || {} - const hostname = options.host || 'localhost' + let hostname: string | undefined + if (options.host === undefined || options.host === 'localhost') { + // Use a secure default + hostname = '127.0.0.1' + } else if (options.host === true) { + // The user probably passed --host in the CLI, without arguments + hostname = undefined // undefined typically means 0.0.0.0 or :: (listen on all IPs) + } else { + hostname = options.host as string + } const protocol = options.https ? 'https' : 'http' const logger = config.logger const base = config.base - httpServer.listen(port, () => { + httpServer.listen(port, hostname, () => { logger.info( chalk.cyan(`\n vite v${require('vite/package.json').version}`) + chalk.green(` build preview server running at:\n`) ) - const interfaces = os.networkInterfaces() - Object.keys(interfaces).forEach((key) => - (interfaces[key] || []) - .filter((details) => details.family === 'IPv4') - .map((detail) => { - return { - type: detail.address.includes('127.0.0.1') - ? 'Local: ' - : 'Network: ', - host: detail.address.replace('127.0.0.1', hostname) - } - }) - .forEach(({ type, host }) => { - const url = `${protocol}://${host}:${chalk.bold(port)}${base}` - logger.info(` > ${type} ${chalk.cyan(url)}`) - }) - ) + if (hostname === '127.0.0.1') { + const url = `${protocol}://localhost:${chalk.bold(port)}${base}` + logger.info(` > Local: ${chalk.cyan(url)}`) + logger.info(` > Network: ${chalk.dim('use `--host` to expose')}`) + } else { + const interfaces = os.networkInterfaces() + Object.keys(interfaces).forEach((key) => + (interfaces[key] || []) + .filter((details) => details.family === 'IPv4') + .map((detail) => { + return { + type: detail.address.includes('127.0.0.1') + ? 'Local: ' + : 'Network: ', + host: detail.address + } + }) + .forEach(({ type, host }) => { + const url = `${protocol}://${host}:${chalk.bold(port)}${base}` + logger.info(` > ${type} ${chalk.cyan(url)}`) + }) + ) + } if (options.open) { const path = typeof options.open === 'string' ? options.open : base diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 2dfbad39541535..5ce3a7dff182de 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -53,7 +53,7 @@ import { ssrRewriteStacktrace } from '../ssr/ssrStacktrace' import { createMissingImporterRegisterFn } from '../optimizer/registerMissing' export interface ServerOptions { - host?: string + host?: string | boolean port?: number /** * Enable TLS + HTTP/2. @@ -531,8 +531,17 @@ async function startServer( const options = server.config.server || {} let port = inlinePort || options.port || 3000 - let hostname = options.host || 'localhost' - if (hostname === '0.0.0.0') hostname = 'localhost' + let hostname: string | undefined + if (options.host === undefined || options.host === 'localhost') { + // Use a secure default + hostname = '127.0.0.1' + } else if (options.host === true) { + // probably passed --host in the CLI, without arguments + hostname = undefined // undefined typically means 0.0.0.0 or :: (listen on all IPs) + } else { + hostname = options.host as string + } + const protocol = options.https ? 'https' : 'http' const info = server.config.logger.info const base = server.config.base @@ -545,7 +554,7 @@ async function startServer( reject(new Error(`Port ${port} is already in use`)) } else { info(`Port ${port} is in use, trying another one...`) - httpServer.listen(++port) + httpServer.listen(++port, hostname) } } else { httpServer.removeListener('error', onError) @@ -555,7 +564,7 @@ async function startServer( httpServer.on('error', onError) - httpServer.listen(port, options.host, () => { + httpServer.listen(port, hostname, () => { httpServer.removeListener('error', onError) info( @@ -565,23 +574,30 @@ async function startServer( clear: !server.config.logger.hasWarned } ) - const interfaces = os.networkInterfaces() - Object.keys(interfaces).forEach((key) => - (interfaces[key] || []) - .filter((details) => details.family === 'IPv4') - .map((detail) => { - return { - type: detail.address.includes('127.0.0.1') - ? 'Local: ' - : 'Network: ', - host: detail.address.replace('127.0.0.1', hostname) - } - }) - .forEach(({ type, host }) => { - const url = `${protocol}://${host}:${chalk.bold(port)}${base}` - info(` > ${type} ${chalk.cyan(url)}`) - }) - ) + + if (hostname === '127.0.0.1') { + const url = `${protocol}://localhost:${chalk.bold(port)}${base}` + info(` > Local: ${chalk.cyan(url)}`) + info(` > Network: ${chalk.dim('use `--host` to expose')}`) + } else { + const interfaces = os.networkInterfaces() + Object.keys(interfaces).forEach((key) => + (interfaces[key] || []) + .filter((details) => details.family === 'IPv4') + .map((detail) => { + return { + type: detail.address.includes('127.0.0.1') + ? 'Local: ' + : 'Network: ', + host: detail.address + } + }) + .forEach(({ type, host }) => { + const url = `${protocol}://${host}:${chalk.bold(port)}${base}` + info(` > ${type} ${chalk.cyan(url)}`) + }) + ) + } // @ts-ignore if (global.__vite_start_time) { diff --git a/scripts/jestPerTestSetup.ts b/scripts/jestPerTestSetup.ts index 08e4fb56dc3dad..54e073d4851a09 100644 --- a/scripts/jestPerTestSetup.ts +++ b/scripts/jestPerTestSetup.ts @@ -73,7 +73,8 @@ beforeAll(async () => { // misses change events, so enforce polling for consistency usePolling: true, interval: 100 - } + }, + host: true }, build: { // skip transpilation and dynamic import polyfills during tests to