如何配置Express vhost实现多Node.js应用同3000端口运行
我在Debian服务器上部署了三个基于Node.js(Express+EJS+视图)的应用文件夹,每个应用单独运行在3000端口时均可正常工作,但无法同时启动运行。目前已配置Nginx反向代理,将443端口的SSL请求转发至Express的3000端口。现请求帮助配置虚拟主机,使这三个应用共享3000端口,且各自使用独立的静态文件与视图文件夹。
报错信息
Error: Failed to lookup view "index" in views directory
"/var/express/vhost/ejs"
at Function.render (/var/express/uc.domain.com/dev/node_modules/express/lib/application.js:597:17)
at ServerResponse.render (/var/express/uc.domain.com/dev/node_modules/express/lib/response.js:1039:7)
at file:///var/express/uc.domain.com/dev/express.js:25:7
at Layer.handle [as handle_request] (/var/express/uc.domain.com/dev/node_modules/express/lib/router/layer.js:95:5)
at next (/var/express/uc.domain.com/dev/node_modules/express/lib/router/route.js:144:13)
at Route.dispatch (/var/express/uc.domain.com/dev/node_modules/express/lib/router/route.js:114:3)
at Layer.handle [as handle_request] (/var/express/uc.domain.com/dev/node_modules/express/lib/router/layer.js:95:5)
at /var/express/uc.domain.com/dev/node_modules/express/lib/router/index.js:284:15
at Function.process_params (/var/express/uc.domain.com/dev/node_modules/express/lib/router/index.js:346:12)
at next (/var/express/uc.domain.com/dev/node_modules/express/lib/router/index.js:280:10)
当前代码
vhost.js
import express from 'express' import vhost from 'vhost' import https from 'https' import fs from 'fs' import path from 'path' import app from '../uc.domain.com/dev/express.js' const options = { key: fs.readFileSync('/var/express/_ssl/helios.uc.domain.com.key'), cert: fs.readFileSync('/var/express/_ssl/helios.uc.domain.com.crt'), requestCert: false, rejectUnauthorized: false, } const port = process.env.PORT || 3000 const app = express() tools.get('/', (req, res) => { res.send('This is the tools.uc.domain.com') }) app.use(vhost('dev.uc.domain.com', app)) app.use(vhost('tools.uc.domain.com', tools)) const server = https.createServer(options, app).listen(port, () => { console.log(`- https://dev.uc.domain.com:${port},`) console.log(`- https://tools.uc.domain.com:${port}`) console.log('For end of work press Ctrl+C') })
../uc.domain.com/dev/express.js
import express from 'express' import https from 'https' import fs from 'fs' import path from 'path' import router from './system/routes.js' const options = { key: fs.readFileSync('/var/express/_ssl/helios.uc.domain.com.key'), cert: fs.readFileSync('/var/express/_ssl/helios.uc.domain.com.crt'), requestCert: false, rejectUnauthorized: false, } const port = process.env.PORT || 3000 const app = express() app.set('view engine', 'ejs') app.set('views', path.resolve(process.cwd(), 'ejs')) app.use(express.static(path.resolve(process.cwd(), 'static'))) app.use(express.json()) app.use(express.urlencoded({ extended: false })) app.use(router) app.get('/', (req, res) => { res.render('index', { title: 'Sweet home Alabama', }) }) app.get('/:sub', (req, res) => { res.render(req.params.sub, { title: 'Sweet home Alabama', }) }) // const server = https.createServer(options, app).listen(port, () => { // console.log(`- https://dev.uc.domain.com:${port}) // }) export default app
问题根源与修复方案
核心问题分析
- 变量重名冲突:vhost.js中先导入dev应用的
app实例,随后又重新声明const app = express(),直接覆盖了原实例,导致虚拟主机配置失效。 - 工作目录错误:express.js用
process.cwd()获取当前目录,当通过vhost主应用启动时,process.cwd()指向vhost.js所在的/var/express/vhost,而非dev应用的/var/express/uc.domain.com/dev,因此找不到ejs和static文件夹。 - 未初始化应用实例:vhost.js中直接使用
tools但未创建对应的Express实例。
修复步骤
1. 修正vhost.js的变量与实例配置
import express from 'express' import vhost from 'vhost' import https from 'https' import fs from 'fs' import path from 'path' // 避免变量重名,重命名导入的dev应用 import devApp from '../uc.domain.com/dev/express.js' // 创建tools应用实例 const toolsApp = express() toolsApp.get('/', (req, res) => { res.send('This is the tools.uc.domain.com') }) const options = { key: fs.readFileSync('/var/express/_ssl/helios.uc.domain.com.key'), cert: fs.readFileSync('/var/express/_ssl/helios.uc.domain.com.crt'), requestCert: false, rejectUnauthorized: false, } const port = process.env.PORT || 3000 const mainApp = express() // 配置虚拟主机规则 mainApp.use(vhost('dev.uc.domain.com', devApp)) mainApp.use(vhost('tools.uc.domain.com', toolsApp)) // 启动主服务器 const server = https.createServer(options, mainApp).listen(port, () => { console.log(`- https://dev.uc.domain.com:${port}`) console.log(`- https://tools.uc.domain.com:${port}`) console.log('For end of work press Ctrl+C') })
2. 修正express.js的路径配置
用import.meta.url获取当前文件的绝对路径,替代process.cwd(),确保指向正确的应用目录:
import express from 'express' import https from 'https' import fs from 'fs' import path from 'path' import router from './system/routes.js' const options = { key: fs.readFileSync('/var/express/_ssl/helios.uc.domain.com.key'), cert: fs.readFileSync('/var/express/_ssl/helios.uc.domain.com.crt'), requestCert: false, rejectUnauthorized: false, } const port = process.env.PORT || 3000 const app = express() app.set('view engine', 'ejs') // 获取当前应用的根目录 const appRoot = path.dirname(new URL(import.meta.url).pathname) // 配置视图文件夹路径 app.set('views', path.join(appRoot, 'ejs')) // 配置静态文件路径 app.use(express.static(path.join(appRoot, 'static'))) app.use(express.json()) app.use(express.urlencoded({ extended: false })) app.use(router) app.get('/', (req, res) => { res.render('index', { title: 'Sweet home Alabama', }) }) app.get('/:sub', (req, res) => { res.render(req.params.sub, { title: 'Sweet home Alabama', }) }) export default app
3. 第三个应用的扩展配置
参照dev应用的模式创建第三个应用的Express实例,在vhost.js中导入并添加虚拟主机规则:
// 在vhost.js中导入第三个应用 import thirdApp from '../path/to/third/app.js' // 添加对应的虚拟主机规则 mainApp.use(vhost('third.uc.domain.com', thirdApp))
4. Nginx反向代理确认
确保Nginx将不同域名的请求转发到Express的3000端口,示例配置:
server { listen 443 ssl; server_name dev.uc.domain.com; ssl_certificate /path/to/ssl/cert; ssl_certificate_key /path/to/ssl/key; location / { proxy_pass https://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } server { listen 443 ssl; server_name tools.uc.domain.com; ssl_certificate /path/to/ssl/cert; ssl_certificate_key /path/to/ssl/key; location / { proxy_pass https://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
内容的提问来源于stack exchange,提问作者user22790531

