Node.js/Express 服务器配置
本指南介绍如何使用 Node.js 和 Express 框架部署 H5 应用,并配置微信小程序域名校验文件的访问。
推荐架构
在生产环境中,推荐使用 Nginx 作为反向代理处理 HTTPS,Node.js 应用监听 HTTP 端口(如 3000)。这样可以让 Nginx 处理 SSL、负载均衡等,Node.js 专注于应用逻辑。
典型架构: 云平台 HTTPS → Nginx (80/443) → Node.js (3000)
前提条件
- 已安装 Node.js(建议版本 18+)
- 已安装 npm 或 yarn
- 已下载微信域名校验文件(如
a1b2c3d4e5f6.txt) - H5 应用已构建完成
校验文件放置方案
与 Nginx 类似,您可以选择两种方案:
方案一:放到 H5 项目根目录(推荐)
将校验文件直接放在 H5 应用的静态资源目录中。
目录结构:
project/
├── public/
│ ├── a1b2c3d4e5f6.txt ← 校验文件(微信生成的随机文件名)
│ ├── index.html
│ └── assets/
│ ├── js/
│ ├── css/
│ └── images/
├── package.json
└── server.js
优点:
- 配置简单,无需额外路由
- 适合大多数场景
缺点:
- 校验文件与业务代码混在一起
方案二:单独目录存放
将校验文件放在专门的目录中。
目录结构:
project/
├── weixin-verify/
│ └── a1b2c3d4e5f6.txt ← 校验文件(微信生成的随机文件名)
├── public/
│ ├── index.html
│ └── assets/
│ ├── js/
│ ├── css/
│ └── images/
├── package.json
└── server.js
优点:
- 结构清晰,便于管理
- 校验文件与业务代码分离
缺点:
- 需要配置额外的路由
Express 配置
方案一配置(校验文件在项目根目录)
推荐用于: 配合 Nginx 反向代理使用
const express = require('express');
const path = require('path');
const app = express();
// 静态文件目录(包含校验文件和 H5 页面)
app.use(express.static(path.join(__dirname, 'public')));
// SPA 路由回退
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// 启动 HTTP 服务器(由 Nginx 处理 HTTPS)
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
配合 Nginx 使用
如果使用 Nginx 作为反向代理,Node.js 只需监听 HTTP 端口。Nginx 配置示例:
server {
listen 443 ssl http2;
server_name h5.example.com;
# SSL 由云平台或 Nginx 处理
location / {
proxy_pass http://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;
}
}
方案二配置(校验文件单独存放)
const express = require('express');
const path = require('path');
const app = express();
// 微信校验文件专用路径(优先匹配 .txt 文件)
// 注意:必须放在其他静态资源之前
app.use('/*.txt', express.static(path.join(__dirname, 'weixin-verify')));
// H5 页面静态资源
app.use(express.static(path.join(__dirname, 'public')));
// SPA 路由回退
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// 启动 HTTP 服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
注意
app.use('/*.txt', ...)确保所有根路径下的 .txt 请求优先匹配到校验文件目录- 路由顺序很重要:校验文件路由 → 静态资源 → SPA 路由
- 必须放在
app.get('*', ...)之前,否则会被 SPA 路由拦截 - 此配置只匹配根路径的 .txt 文件(如
/xxx.txt),不影响子目录的 .txt 文件
完整示例项目
package.json
{
"name": "h5-miniprogram-server",
"version": "1.0.0",
"description": "H5 server for WeChat MiniProgram WebView",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.2",
"helmet": "^7.1.0",
"compression": "^1.7.4"
},
"devDependencies": {
"nodemon": "^3.0.2"
}
}
server.js(生产环境增强版)
const express = require('express');
const path = require('path');
const helmet = require('helmet');
const compression = require('compression');
const app = express();
// 安全头
app.use(helmet({
contentSecurityPolicy: false, // 根据实际需求配置
}));
// Gzip 压缩
app.use(compression());
// 日志中间件
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
next();
});
// 微信校验文件路由(方案二,如使用方案一可删除此段)
app.use('/*.txt', express.static(path.join(__dirname, 'weixin-verify')));
// 静态资源配置
app.use(express.static(path.join(__dirname, 'public'), {
maxAge: '1y', // 缓存 1 年
etag: true,
lastModified: true,
setHeaders: (res, filePath) => {
// HTML 文件不缓存
if (filePath.endsWith('.html')) {
res.setHeader('Cache-Control', 'no-cache');
}
}
}));
// 健康检查端点
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() });
});
// SPA 路由回退
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(500).json({ error: 'Internal Server Error' });
});
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`✅ Server running on port ${PORT}`);
});
使用环境变量
创建 .env 文件(推荐使用 dotenv 包):
# .env
PORT=3000
NODE_ENV=production
// 在 server.js 顶部添加
require('dotenv').config();
部署步骤
1. 初始化项目
# 创建项目目录
mkdir h5-miniprogram-server
cd h5-miniprogram-server
# 初始化 package.json
npm init -y
# 安装依赖
npm install express helmet compression
npm install --save-dev nodemon
2. 准备文件
# 创建目录(方案一)
mkdir public
# 或(方案二)
mkdir public weixin-verify
# 将 H5 构建产物复制到 public/
# 将校验文件复制到对应目录
3. 启动服务
# 开发环境
npm run dev
# 生产环境
npm start
使用 PM2 管理进程
安装 PM2
npm install -g pm2
PM2 配置文件
创建 ecosystem.config.js:
module.exports = {
apps: [{
name: 'h5-server',
script: './server.js',
instances: 'max', // 使用所有 CPU 核心
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
merge_logs: true,
autorestart: true,
max_memory_restart: '1G'
}]
};
PM2 命令
# 启动应用
pm2 start ecosystem.config.js
# 查看状态
pm2 status
# 查看日志
pm2 logs h5-server
# 重启
pm2 restart h5-server
# 停止
pm2 stop h5-server
# 设置开机自启
pm2 startup
pm2 save
验证配置
验证校验文件可访问
# 使用 curl 测试(替换为实际文件名)
curl https://h5.example.com/a1b2c3d4e5f6.txt
# 查看详细响应
curl -I https://h5.example.com/a1b2c3d4e5f6.txt
# 预期输出:
# HTTP/2 200
# content-type: text/plain
验证 H5 页面可访问
# 测试主页
curl -I https://h5.example.com
# 预期返回 200 状态码
验证 HTTPS 重定向
# 测试 HTTP 是否自动跳转到 HTTPS
curl -I http://h5.example.com
# 预期输出:
# HTTP/1.1 301 Moved Permanently
# Location: https://h5.example.com/
常见问题
端口权限问题
在 Linux 上,非 root 用户无法监听 80 和 443 端口。
推荐方案:使用 Nginx 反向代理
Node.js 监听 3000 端口,Nginx 监听 80/443 并转发到 3000。参考前面的 Nginx 配置示例。
备选方案:使用 authbind
# 安装 authbind
sudo apt-get install authbind
# 允许 Node.js 使用 80 和 443 端口
sudo touch /etc/authbind/byport/80
sudo touch /etc/authbind/byport/443
sudo chmod 500 /etc/authbind/byport/80
sudo chmod 500 /etc/authbind/byport/443
sudo chown youruser /etc/authbind/byport/80
sudo chown youruser /etc/authbind/byport/443
# 使用 authbind 启动
authbind --deep node server.js
文件找不到
检查路径:
// 添加调试日志
console.log('__dirname:', __dirname);
console.log('Public path:', path.join(__dirname, 'public'));
// 检查文件是否存在
const fs = require('fs');
const publicPath = path.join(__dirname, 'public');
console.log('Files in public:', fs.readdirSync(publicPath));
相关资源
- 📖 小程序配置指南
- 🔗 Express 官方文档
- 🔗 PM2 官方文档