Skip to main content

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.

Recommended Architecture

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:

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}`);
});
Use with Nginx

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}`);
});
Note
  • 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));