You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于域名在Express Router中条件加载路由实现多站点部署?

问题

我知道很多人会建议我"重新考虑应用架构",但请不要这么做。该方案契合我的实际需求,希望能得到实用的解决方案。

我已有代码可根据域名提供不同静态文件,实现同路由下多静态HTML站点的运行:

app.get('/', (req, res) => {
  console.log('req.hostname: ', req.hostname)
  if (req.hostname === 'localhost') {
    app.use(express.static(path.join(__dirname, 'pages/site-A/')));
    res.sendFile(path.join(__dirname, 'pages/site-A/index.html'));
  } else {
    app.use(express.static(path.join(__dirname, 'pages/default/')));
    res.sendFile(path.join(__dirname, 'pages/default/index.html'));
  }
});

这段代码运行正常。但我正在开发的用于管理这些站点的主站点更为复杂,它拥有独立的路由。通常我会通过以下方式引入该路由:

app.use('/', require('pages/site/routes'));

请问如何基于域名在'/'路由中条件性地使用我的路由文件?

app.get('/', (req, res) => {
  console.log('req.hostname: ', req.hostname)
  if (req.hostname === 'localhost') {

    app.use(express.static(path.join(__dirname, 'pages/site-A/')));
    res.sendFile(path.join(__dirname, 'pages/site-A/index.html'));

  } else if (req.hostname === 'other.local') {

    // equivalent of res.send but for app.use('/', require('pages/site/routes')); 

  } else {

    app.use(express.static(path.join(__dirname, 'pages/default/')));
    res.sendFile(path.join(__dirname, 'pages/default/index.html'));

  }
});
解决方案

不能在app.get('/')的回调内调用app.use——app.use是用来注册全局/路由级中间件的,会在应用启动阶段生效,而非每次请求时动态注册。下面是两种实用的实现方式:

方法一:域名过滤中间件+路由挂载

提前引入管理站点的路由模块,通过中间件判断域名,仅匹配other.local时才走该路由:

// 提前引入路由模块,避免重复加载
const adminRoutes = require('pages/site/routes');

// 域名判断中间件
const checkAdminDomain = (req, res, next) => {
  if (req.hostname === 'other.local') {
    // 域名匹配,继续执行管理路由
    return next();
  }
  // 域名不匹配,跳过当前路由栈,去匹配下一个同路径的处理逻辑
  next('route');
};

// 挂载带域名校验的管理路由
app.use('/', checkAdminDomain, adminRoutes);

// 处理静态站点的根路由逻辑
app.get('/', (req, res) => {
  console.log('req.hostname: ', req.hostname);
  let staticPath, indexPath;
  if (req.hostname === 'localhost') {
    staticPath = path.join(__dirname, 'pages/site-A/');
    indexPath = path.join(staticPath, 'index.html');
  } else {
    staticPath = path.join(__dirname, 'pages/default/');
    indexPath = path.join(staticPath, 'index.html');
  }
  // 注意:不要在请求回调里重复注册static中间件,建议提前注册
  app.use(express.static(staticPath));
  res.sendFile(indexPath);
});

next('route')是关键:它会让Express跳过当前路由链中剩余的中间件,直接匹配下一个同路径的路由规则(也就是后面的app.get('/'))。

方法二:分域名隔离所有路由逻辑

如果想让代码结构更清晰,可以把每个域名对应的处理逻辑都用中间件隔离:

const adminRoutes = require('pages/site/routes');

// 处理localhost域名的静态站点
app.use('/', (req, res, next) => {
  if (req.hostname !== 'localhost') return next('route');
  const staticPath = path.join(__dirname, 'pages/site-A/');
  app.use(express.static(staticPath));
  res.sendFile(path.join(staticPath, 'index.html'));
});

// 处理other.local域名的管理路由
app.use('/', (req, res, next) => {
  if (req.hostname !== 'other.local') return next('route');
  next();
}, adminRoutes);

// 处理默认域名的静态站点
app.use('/', (req, res) => {
  const staticPath = path.join(__dirname, 'pages/default/');
  app.use(express.static(staticPath));
  res.sendFile(path.join(staticPath, 'index.html'));
});

重要优化提示

你原代码存在一个隐患:每次请求/时都调用app.use(express.static(...)),这会重复注册静态文件中间件,长期运行会导致内存泄漏。建议提前一次性注册所有静态目录:

// 提前注册所有静态目录,避免重复注册
app.use(express.static(path.join(__dirname, 'pages/site-A/')));
app.use(express.static(path.join(__dirname, 'pages/default/')));

// 根路由仅负责根据域名返回对应index.html
app.get('/', (req, res) => {
  let indexPath;
  if (req.hostname === 'localhost') {
    indexPath = path.join(__dirname, 'pages/site-A/index.html');
  } else {
    indexPath = path.join(__dirname, 'pages/default/index.html');
  }
  res.sendFile(indexPath);
});

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 16:05:02