
本文介绍如何在 Express 中通过自定义中间件实现基于 Host 请求头的条件路由,使单个服务能根据访问域名精准分发至不同静态资源或路由模块,适用于本地多站开发与生产环境多域名托管场景。
本文介绍如何在 express 中通过自定义中间件实现基于 `host` 请求头的条件路由,使单个服务能根据访问域名精准分发至不同静态资源或路由模块,适用于本地多站开发与生产环境多域名托管场景。
在 Express 应用中,app.use() 是全局注册中间件或子路由的标准方式,但它本身不具备运行时条件判断能力——无法直接在 app.get('/') 回调中“动态挂载”一个 Router 实例(因为 app.use() 必须在请求处理前声明)。因此,将路由逻辑封装为可复用、可配置的中间件函数,是实现“按域名加载对应 Router”的最佳实践。
核心思路是:编写一个高阶中间件工厂函数,接收目标域名列表和待激活的路由处理器(Router 或静态中间件),在每次请求时检查 req.headers.host,仅当匹配时才将请求交由该处理器处理;否则调用 next() 继续后续中间件链。
以下是推荐实现:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
// middleware/domainRouter.js
module.exports = (hosts, handler) => {
return (req, res, next) => {
const host = req.headers.host?.split(':')[0] || ''; // 忽略端口,兼容 localhost:3000
if (hosts.includes(host)) {
// 直接调用 handler,支持 Router 实例或函数式中间件
if (typeof handler === 'function') {
handler(req, res, next);
} else {
// 若 handler 是 Express Router,则需确保已正确设置其内部中间件
handler(req, res, next);
}
} else {
next();
}
};
};在主服务文件(如 app.js)中使用:
const express = require('express');
const app = express();
const domainRouter = require('./middleware/domainRouter');
// ✅ 站点 A:使用自定义 Router(如 pages/site-A/routes.js)
app.use('/', domainRouter(
['localhost', 'site-a.local', 'site-a.com'],
require('./pages/site-A/routes')
));
// ✅ 站点 B:直接托管静态 HTML(注意:此处需用中间件函数包装 static)
app.use('/', domainRouter(
['site-b.local', 'site-b.com'],
express.static(path.join(__dirname, 'pages/site-b-html'))
));
// ✅ 站点 C:混合使用(如 React SSR 路由)
const siteCRoutes = require('./pages/site-C/routes');
app.use('/', domainRouter(
['site-c.local', 'site-c.com'],
siteCRoutes
));
// ⚠️ 注意:所有 domainRouter 中间件必须在其他全局中间件(如 bodyParser、cors)之后、错误处理之前注册
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// ❌ 错误示例:不要在路由回调内调用 app.use()
// app.get('/', (req, res) => {
// if (req.hostname === 'site-a.com') {
// app.use(require('./routes')); // ❌ 运行时挂载无效且危险!
// }
// });
// ✅ 默认兜底:未匹配任何域名时返回默认站点
app.use(express.static(path.join(__dirname, 'pages/default')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'pages/default/index.html'));
});关键注意事项:
- req.headers.host 是标准 HTTP 请求头,包含主机名和可选端口(如 localhost:3000),建议用 .split(':')[0] 提取纯域名进行比对;
- 所有 domainRouter 实例均应注册为 app.use() 的顶层中间件,而非嵌套在 app.get() 内,确保每次请求都能被统一拦截;
- 静态资源中间件(如 express.static)可直接传入 domainRouter,但需确认路径存在且权限正确;
- 开发时可通过修改系统 /etc/hosts 文件(如 127.0.0.1 site-a.local)模拟多域名访问,无需额外代理;
- 生产部署时,确保反向代理(如 Nginx)正确透传 Host 头,避免被覆盖为上游地址。
此方案轻量、可扩展、符合 Express 中间件设计哲学,让你用一套代码优雅支撑多个独立站点,兼顾开发效率与线上稳定性。


















