Node.js/Express Server Configuration
This guide describes how to deploy H5 applications using Node.js and Express framework, and configure WeChat MiniProgram domain name verification file access.
In production environments, it's recommended to use Nginx as a reverse proxy to handle HTTPS, with the Node.js application listening on HTTP port (such as 3000). This allows Nginx to handle SSL, load balancing, etc., while Node.js focuses on application logic.
Typical architecture: Cloud platform HTTPS → Nginx (80/443) → Node.js (3000)
Prerequisites
- Node.js installed (recommended version 18+)
- npm or yarn installed
- WeChat domain name verification file downloaded (such as
a1b2c3d4e5f6.txt) - H5 application built
Verification File Placement Options
Similar to Nginx, you can choose one of two options:
Option 1: Place in H5 Project Root Directory (Recommended)
Place the verification file directly in the H5 application's static resource directory.
Directory Structure:
project/
├── public/
│ ├── a1b2c3d4e5f6.txt ← Verification file (random filename generated by WeChat)
│ ├── index.html
│ └── assets/
│ ├── js/
│ ├── css/
│ └── images/
├── package.json
└── server.js
Advantages:
- Simple configuration, no additional routing needed
- Suitable for most scenarios
Disadvantages:
- Verification file mixed with business code
Option 2: Separate Directory
Place the verification file in a dedicated directory.
Directory Structure:
project/
├── weixin-verify/
│ └── a1b2c3d4e5f6.txt ← Verification file (random filename generated by WeChat)
├── public/
│ ├── index.html
│ └── assets/
│ ├── js/
│ ├── css/
│ └── images/
├── package.json
└── server.js
Advantages:
- Clear structure, easy to manage
- Verification file separated from business code
Disadvantages:
- Requires additional routing configuration
Express Configuration
Option 1 Configuration (Verification file in project root directory)
Recommended for: Use with Nginx reverse proxy
const express = require('express');
const path = require('path');
const app = express();
// Static file directory (contains verification file and H5 pages)
app.use(express.static(path.join(__dirname, 'public')));
// SPA route fallback
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// Start HTTP server (HTTPS handled by Nginx)
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
If using Nginx as a reverse proxy, Node.js only needs to listen on HTTP port. Nginx configuration example:
server {
listen 443 ssl http2;
server_name h5.example.com;
# SSL handled by cloud platform or 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;
}
}
Option 2 Configuration (Verification file stored separately)
const express = require('express');
const path = require('path');
const app = express();
// WeChat verification file dedicated path (prioritize matching .txt files)
// Note: Must be placed before other static resources
app.use('/*.txt', express.static(path.join(__dirname, 'weixin-verify')));
// H5 page static resources
app.use(express.static(path.join(__dirname, 'public')));
// SPA route fallback
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// Start HTTP server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
app.use('/*.txt', ...)ensures all .txt requests at root path are prioritized to match the verification file directory- Routing order is important: verification file route → static resources → SPA route
- Must be placed before
app.get('*', ...), otherwise it will be intercepted by SPA route - This configuration only matches .txt files at root path (such as
/xxx.txt), does not affect .txt files in subdirectories
Complete Example Project
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 (Production Enhanced Version)
const express = require('express');
const path = require('path');
const helmet = require('helmet');
const compression = require('compression');
const app = express();
// Security headers
app.use(helmet({
contentSecurityPolicy: false, // Configure according to actual needs
}));
// Gzip compression
app.use(compression());
// Logging middleware
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
next();
});
// WeChat verification file route (Option 2, can delete this section if using Option 1)
app.use('/*.txt', express.static(path.join(__dirname, 'weixin-verify')));
// Static resource configuration
app.use(express.static(path.join(__dirname, 'public'), {
maxAge: '1y', // Cache for 1 year
etag: true,
lastModified: true,
setHeaders: (res, filePath) => {
// Don't cache HTML files
if (filePath.endsWith('.html')) {
res.setHeader('Cache-Control', 'no-cache');
}
}
}));
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() });
});
// SPA route fallback
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(500).json({ error: 'Internal Server Error' });
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`✅ Server running on port ${PORT}`);
});
Using Environment Variables
Create .env file (recommend using dotenv package):
# .env
PORT=3000
NODE_ENV=production
// Add at top of server.js
require('dotenv').config();
Deployment Steps
1. Initialize Project
# Create project directory
mkdir h5-miniprogram-server
cd h5-miniprogram-server
# Initialize package.json
npm init -y
# Install dependencies
npm install express helmet compression
npm install --save-dev nodemon
2. Prepare Files
# Create directories (Option 1)
mkdir public
# Or (Option 2)
mkdir public weixin-verify
# Copy H5 build artifacts to public/
# Copy verification file to corresponding directory
3. Start Service
# Development environment
npm run dev
# Production environment
npm start
Using PM2 to Manage Processes
Install PM2
npm install -g pm2
PM2 Configuration File
Create ecosystem.config.js:
module.exports = {
apps: [{
name: 'h5-server',
script: './server.js',
instances: 'max', // Use all CPU cores
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 Commands
# Start application
pm2 start ecosystem.config.js
# View status
pm2 status
# View logs
pm2 logs h5-server
# Restart
pm2 restart h5-server
# Stop
pm2 stop h5-server
# Set up auto-start on boot
pm2 startup
pm2 save
Verify Configuration
Verify Verification File is Accessible
# Test with curl (replace with actual filename)
curl https://h5.example.com/a1b2c3d4e5f6.txt
# View detailed response
curl -I https://h5.example.com/a1b2c3d4e5f6.txt
# Expected output:
# HTTP/2 200
# content-type: text/plain
Verify H5 Page is Accessible
# Test homepage
curl -I https://h5.example.com
# Expected to return 200 status code
Verify HTTPS Redirect
# Test if HTTP automatically redirects to HTTPS
curl -I http://h5.example.com
# Expected output:
# HTTP/1.1 301 Moved Permanently
# Location: https://h5.example.com/
Common Issues
Port Permission Issues
On Linux, non-root users cannot listen on ports 80 and 443.
Recommended Solution: Use Nginx Reverse Proxy
Node.js listens on port 3000, Nginx listens on ports 80/443 and forwards to 3000. Refer to the previous Nginx configuration example.
Alternative Solution: Use authbind
# Install authbind
sudo apt-get install authbind
# Allow Node.js to use ports 80 and 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
# Start with authbind
authbind --deep node server.js
File Not Found
Check Path:
// Add debug logging
console.log('__dirname:', __dirname);
console.log('Public path:', path.join(__dirname, 'public'));
// Check if file exists
const fs = require('fs');
const publicPath = path.join(__dirname, 'public');
console.log('Files in public:', fs.readdirSync(publicPath));