@@ -4,6 +4,7 @@ import tailwindcss from '@tailwindcss/vite';
44import boxen from 'boxen' ;
55import chalk from 'chalk' ;
66import dns from 'node:dns/promises' ;
7+ import net from 'node:net' ;
78import os from 'node:os' ;
89import { env } from 'node:process' ;
910import license from 'rollup-plugin-license' ;
@@ -149,9 +150,62 @@ async function getServerConfig(mode: string, useLocalRedirect: boolean) {
149150 // Ensure we have the host entry redirecting to localhost
150151 await ensureFqdnRedirect ( '127.0.0.1' , host ) ;
151152
153+ // Check if we can bind to port 443 before Vite tries and fails with an unhelpful error
154+ await ensurePortBindable ( host , 443 ) ;
155+
152156 return { host, port : 443 , proxy : { } } ;
153157}
154158
159+ async function ensurePortBindable ( host : string , port : number ) : Promise < void > {
160+ return new Promise ( ( resolve ) => {
161+ const server = net . createServer ( ) ;
162+ server . once ( 'error' , ( err : NodeJS . ErrnoException ) => {
163+ if ( err . code === 'EACCES' ) {
164+ const platform = os . platform ( ) ;
165+ let fix : string ;
166+
167+ if ( platform === 'linux' ) {
168+ fix = [
169+ `Node.js needs permission to serve HTTPS on port ${ port } .\n` ,
170+ 'Option 1 (Recommended): Set up a reverse proxy' ,
171+ ' Use Nginx or Caddy to proxy traffic to your Node server.' ,
172+ ' This is more secure and follows best practices.\n' ,
173+ 'Option 2 (Quick fix): Grant Node.js permission to bind to privileged ports' ,
174+ chalk . blue . bold ( ` sudo setcap 'cap_net_bind_service=+ep' $(which node)\n` ) ,
175+ chalk . yellow . bold ( ' ⚠️ Security note: This allows Node to bind to ANY port below 1024.' ) ,
176+ chalk . yellow . bold ( ' Only use this in trusted development environments.' ) ,
177+ ] . join ( '\n' ) ;
178+ } else if ( platform === 'darwin' ) {
179+ fix = [
180+ `Node.js needs permission to serve HTTPS on port ${ port } .\n` ,
181+ 'Fix: Run the dev server with sudo, or set up a reverse proxy.' ,
182+ ] . join ( '\n' ) ;
183+ } else {
184+ fix = `Node.js does not have permission to bind to port ${ port } .\nTry running with elevated privileges or use a reverse proxy.` ;
185+ }
186+
187+ console . log (
188+ boxen ( fix , {
189+ padding : 1 ,
190+ margin : 1 ,
191+ borderStyle : 'round' ,
192+ borderColor : 'yellow' ,
193+ title : `Port ${ port } Permission Denied` ,
194+ titleAlignment : 'center' ,
195+ } )
196+ ) ;
197+ process . exit ( 1 ) ;
198+ }
199+ // For other errors (e.g. EADDRINUSE), let Vite handle them
200+ resolve ( ) ;
201+ } ) ;
202+ server . once ( 'listening' , ( ) => {
203+ server . close ( ( ) => resolve ( ) ) ;
204+ } ) ;
205+ server . listen ( port , host ) ;
206+ } ) ;
207+ }
208+
155209export default defineConfig ( async ( { command, mode, isPreview } ) => {
156210 const isLocalServe = command === 'serve' || isPreview === true ;
157211 const isProduction = mode === 'production' && ( isTruthy ( env . DOCKER ) || isTruthy ( env . CF_PAGES ) ) ;
0 commit comments