部署Next.js到cPanel+LiteSpeed遇http.Server.listen()重复调用错误求助
Next.js 13.4.19 部署至 cPanel+LiteSpeed 共享主机报错问题
问题背景
将版本为13.4.19的Next.js应用部署到cPanel+LiteSpeed共享主机时出现错误。由于cPanel环境限制,无法通过next start启动应用,需使用独立构建生成的server.js作为入口脚本。经排查,怀疑LiteSpeed自带的/usr/local/lsws/fcgi-bin/lsnode.js启动脚本与Next.js存在兼容性冲突。
报错信息
Error: http.Server.listen() was called more than once which is not allowed. at Server.customListen [as listen] (/usr/local/lsws/fcgi-bin/lsnode.js:68:15) at ipcPort (/home/[..webbroot..]/node_modules/next/dist/server/lib/server-ipc.js:73:17) at new Promise (<anonymous>) at createIpcServer (/home/[..webbroot..]/node_modules/next/dist/server/lib/server-ipc.js:72:27) at initialize (/home/[..webbroot..]/node_modules/next/dist/server/lib/router-server.js:79:82) at async Server.<anonymous> (/home/[..webbroot..]/node_modules/next/dist/server/lib/start-server.js:192:36)
核心问题
- 是否有用户在同类环境部署Next.js时遇到过相同问题?
- 有哪些可行方案解决此兼容性问题,实现Next.js与LiteSpeed的协同运行?
调研进展
曾查阅Phusion Passenger关于反向端口绑定的相关文档,但未成功适配到Next.js场景中。
lsnode.js脚本源代码
/* * Copyright 2002-2018 Lite Speed Technologies Inc, All Rights Reserved. * LITE SPEED PROPRIETARY/CONFIDENTIAL. */ var EventEmitter = require('events').EventEmitter; var os = require('os'); var fs = require('fs'); var http = require('http'); var util = require('util'); var net = require('net'); var socketObject = { fd: 0 }; module.isApplicationLoader = true; global.LsNode = new EventEmitter(); startApplication(); function startApplication() { var appRoot = process.env.LSNODE_ROOT || process.cwd(); var startupFile = process.env.LSNODE_STARTUP_FILE || 'app.js'; LsNode.listenDone = false; if (process.env.LSNODE_ROOT != undefined) { try { process.chdir(process.env.LSNODE_ROOT); } catch (err) { console.error("Error setting directory to: " + process.env.LSNODE_ROOT + ": " + err); } } if (!startupFile.startsWith('/')) { startupFile = appRoot + '/' + startupFile; } process.title = 'lsnode:' + appRoot; var consoleLog = process.env.LSNODE_CONSOLE_LOG || '/dev/null'; fs.closeSync(1); try { fs.openSync(consoleLog, "w+"); } catch(e) { fs.openSync('/dev/null', "w+"); } http.Server.prototype.realListen = http.Server.prototype.listen; http.Server.prototype.listen = customListen; http.Server.prototype.address = lsnode_address; var app = require(startupFile); if (!LsNode.listenDone) { if (typeof app.listen === "function") app.listen(3000); } } function lsnode_address() { return process.env.LSNODE_SOCKET; } function customListen(port) { function onListenError(error) { server.emit('error', error); } // The replacement for the listen call! var server = this; if (LsNode.listenDone) { throw new Error("http.Server.listen() was called more than once " + "which is not allowed."); } LsNode.listenDone = true; var listeners = server.listeners('request'); var i; server.removeAllListeners('request'); server.on('request', function(req) { req.connection.__defineGetter__('remoteAddress', function() { return '127.0.0.1'; }); req.connection.__defineGetter__('remotePort', function() { return port; }); req.connection.__defineGetter__('addressType', function() { return 4; }); }); for (i = 0; i < listeners.length; i++) { server.on('request', listeners[i]); } var callback; if (arguments.length > 1 && typeof(arguments[arguments.length - 1]) == 'function') { callback = arguments[arguments.length - 1]; } server.once('error', onListenError); server.realListen(socketObject, function() { server.removeListener('error', onListenError); if (callback) { server.once('listening', callback); } server.emit('listening'); }); return server; }
问题分析与解决方案
问题根源
从报错栈和lsnode.js源码可以看出:LiteSpeed的customListen方法会拦截http.Server.listen调用,设置LsNode.listenDone = true后禁止后续再次调用。但Next.js启动时,除主服务器外还会创建IPC服务器(对应server-ipc.js逻辑),导致第二次调用listen触发错误。
可行解决方案
- 禁用Next.js的IPC服务器:在
server.js入口添加配置,通过环境变量或修改构建后的代码跳过IPC服务器初始化。例如启动时传入--disable-ipc参数(若Next.js支持),或手动注释createIpcServer相关逻辑。 - 绕过LiteSpeed的listen拦截:在
server.js最顶部保存原始http.Server.listen方法,恢复后再启动Next.js,待所有listen调用完成后按需重新应用拦截:// server.js顶部添加 const originalListen = require('http').Server.prototype.listen; require('http').Server.prototype.listen = originalListen; // 导入并启动Next.js const { createServer } = require('next'); const app = createServer({ /* 你的应用配置 */ }); - 切换静态导出模式:若应用无需SSR、API路由等服务端功能,使用
next export生成纯静态文件,直接部署到cPanel静态目录,彻底绕过Node.js运行时问题。 - 联系主机服务商更新环境:当前
lsnode.js版本标注为2018年,可能存在兼容性滞后。可联系服务商询问是否有更新版本,或是否支持用Passenger替代lsnode作为Node.js运行时。 - 自定义适配脚本:编写中间启动脚本,先处理LiteSpeed的listen拦截逻辑,再启动Next.js,手动管理
LsNode.listenDone状态以兼容多次listen调用。
内容的提问来源于stack exchange,提问作者Ivar
相关产品推荐
相关产品推荐

