Express.js Deploy:Node.js Express 專案部署攻略【2025】

Express.js Deploy:Node.js Express 專案部署攻略

Express.js 是 Node.js 生態系統中最受歡迎的 Web 框架,輕量、彈性、擁有龐大的中間件生態系統。無論是建立 RESTful API、GraphQL Server 還是全端 Web 應用,Express 都能勝任。

但從本地開發到生產環境,Express 需要加上許多安全性與效能的中間件。這篇教學將帶你了解 Express 專案的生產環境最佳實踐,以及如何部署到各大平台。

如果你想了解 Node.js 的多種部署方式,可以先閱讀 Node.js 部署教學


Express 生產環境必備設定

express-production-stack

插圖:Express.js 生產環境技術棧圖

場景描述:
Technical illustration showing Express.js 生產環境技術棧圖. Clean, professional diagram style.

視覺重點:
- Clear presentation of the concept
- Professional technical diagram style
- Easy to understand visual elements

必須出現的元素:
- Relevant technical components
- Clear labels and annotations
- Logical flow or structure

需要顯示的中文字:

風格:
Clean flat design, technical illustration, modern infographic style

顏色調性:
Professional blues and grays, with accent colors for emphasis

避免元素:
Cluttered design, realistic photos, complex 3D rendering

在部署之前,你的 Express 應用需要加入生產環境必備的中間件與設定。

專案結構

一個生產就緒的 Express 專案結構:

my-express-api/
├── src/
│   ├── index.js           # 應用程式入口
│   ├── app.js             # Express app 設定
│   ├── routes/
│   │   ├── index.js
│   │   └── users.js
│   ├── controllers/
│   ├── middlewares/
│   │   ├── errorHandler.js
│   │   └── auth.js
│   ├── services/
│   └── utils/
├── package.json
├── ecosystem.config.js    # PM2 設定
├── Dockerfile
├── .env.example
└── .gitignore

package.json 設定

{
  "name": "my-express-api",
  "version": "1.0.0",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "pm2:start": "pm2 start ecosystem.config.js",
    "pm2:stop": "pm2 stop all"
  },
  "engines": {
    "node": ">=20.0.0"
  },
  "dependencies": {
    "express": "^4.18.2",
    "cors": "^2.8.5",
    "helmet": "^7.1.0",
    "express-rate-limit": "^7.1.5",
    "compression": "^1.7.4",
    "morgan": "^1.10.0",
    "dotenv": "^16.3.1"
  },
  "devDependencies": {
    "nodemon": "^3.0.2"
  }
}

生產環境中間件設定

express-middleware-flow

插圖:Express 中間件執行流程圖

場景描述:
Technical illustration showing Express 中間件執行流程圖. Clean, professional diagram style.

視覺重點:
- Clear presentation of the concept
- Professional technical diagram style
- Easy to understand visual elements

必須出現的元素:
- Relevant technical components
- Clear labels and annotations
- Logical flow or structure

需要顯示的中文字:

風格:
Clean flat design, technical illustration, modern infographic style

顏色調性:
Professional blues and grays, with accent colors for emphasis

避免元素:
Cluttered design, realistic photos, complex 3D rendering

中間件的順序很重要,以下是推薦的設定方式。

完整的 app.js 設定

// src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const rateLimit = require('express-rate-limit');
const morgan = require('morgan');

const app = express();

// ============================
// 安全性中間件
// ============================

// Helmet: 設定各種 HTTP 標頭以增強安全性
app.use(helmet());

// CORS: 跨域資源共享
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400 // 預檢請求快取 24 小時
}));

// Rate Limiting: 限制請求頻率
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 分鐘
  max: 100, // 每個 IP 最多 100 次請求
  message: {
    error: 'Too many requests, please try again later.'
  },
  standardHeaders: true,
  legacyHeaders: false,
});
app.use('/api/', limiter);

// ============================
// 效能中間件
// ============================

// Compression: 壓縮回應
app.use(compression());

// ============================
// 解析中間件
// ============================

// 解析 JSON
app.use(express.json({ limit: '10mb' }));

// 解析 URL-encoded
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

// ============================
// 日誌中間件
// ============================

// Morgan: HTTP 請求日誌
if (process.env.NODE_ENV === 'production') {
  app.use(morgan('combined'));
} else {
  app.use(morgan('dev'));
}

// ============================
// 健康檢查
// ============================

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString() });
});

app.get('/ready', (req, res) => {
  // 可以在這裡檢查資料庫連線等
  res.status(200).json({ status: 'ready' });
});

// ============================
// API 路由
// ============================

const routes = require('./routes');
app.use('/api', routes);

// ============================
// 404 處理
// ============================

app.use((req, res) => {
  res.status(404).json({ error: 'Not Found' });
});

// ============================
// 錯誤處理
// ============================

app.use((err, req, res, next) => {
  console.error(err.stack);

  const statusCode = err.statusCode || 500;
  const message = process.env.NODE_ENV === 'production'
    ? 'Internal Server Error'
    : err.message;

  res.status(statusCode).json({ error: message });
});

module.exports = app;

入口檔案

// src/index.js
require('dotenv').config();

const app = require('./app');

const PORT = process.env.PORT || 3000;

const server = app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
  console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});

// 優雅關閉
process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down gracefully');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

process.on('SIGINT', () => {
  console.log('SIGINT received, shutting down gracefully');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

各中間件作用說明

中間件 作用 重要性
helmet 設定安全相關的 HTTP 標頭 必備
cors 處理跨域請求 必備(API)
express-rate-limit 防止暴力攻擊與濫用 必備
compression gzip 壓縮回應 建議
morgan HTTP 請求日誌 建議

💡 Express API 部署遇到問題?

從中間件設定到安全性加固,Express 生產部署有許多細節。

預約免費諮詢,讓我們幫你處理 Express API 部署。


PM2 程序管理

pm2-ecosystem-config

插圖:PM2 ecosystem.config.js 設定圖

場景描述:
Technical illustration showing PM2 ecosystem.config.js 設定圖. Clean, professional diagram style.

視覺重點:
- Clear presentation of the concept
- Professional technical diagram style
- Easy to understand visual elements

必須出現的元素:
- Relevant technical components
- Clear labels and annotations
- Logical flow or structure

需要顯示的中文字:

風格:
Clean flat design, technical illustration, modern infographic style

顏色調性:
Professional blues and grays, with accent colors for emphasis

避免元素:
Cluttered design, realistic photos, complex 3D rendering

PM2 是 Node.js 生產環境的標準程序管理工具,提供自動重啟、負載平衡、日誌管理等功能。

安裝 PM2

npm install -g pm2

ecosystem.config.js 完整設定

// ecosystem.config.js
module.exports = {
  apps: [{
    // 應用程式名稱
    name: 'my-express-api',

    // 入口檔案
    script: './src/index.js',

    // Cluster 模式:使用所有 CPU 核心
    instances: 'max',
    exec_mode: 'cluster',

    // 自動重啟設定
    autorestart: true,
    watch: false,
    max_memory_restart: '1G',

    // 環境變數(開發)
    env: {
      NODE_ENV: 'development',
      PORT: 3000
    },

    // 環境變數(生產)
    env_production: {
      NODE_ENV: 'production',
      PORT: 3000
    },

    // 日誌設定
    log_date_format: 'YYYY-MM-DD HH:mm:ss',
    error_file: './logs/error.log',
    out_file: './logs/out.log',
    merge_logs: true,

    // 進階設定
    listen_timeout: 8000,
    kill_timeout: 5000,

    // 重啟策略
    max_restarts: 10,
    restart_delay: 4000,

    // 每處理 1000 個請求後重啟(防止記憶體洩漏)
    max_requests: 1000,

    // Source Map 支援
    source_map_support: true
  }]
};

PM2 常用命令

# 啟動應用(開發環境)
pm2 start ecosystem.config.js

# 啟動應用(生產環境)
pm2 start ecosystem.config.js --env production

# 查看狀態
pm2 status

# 查看日誌
pm2 logs my-express-api
pm2 logs --lines 100

# 監控
pm2 monit

# 零停機更新
pm2 reload my-express-api

# 停止
pm2 stop my-express-api

# 刪除
pm2 delete my-express-api

# 開機自動啟動
pm2 startup
pm2 save

Cluster Mode vs Fork Mode

模式 說明 適用場景
Cluster 多程序,自動負載平衡 多核心 CPU、高流量
Fork 單程序 開發環境、低流量
// Cluster Mode(推薦生產環境)
instances: 'max',
exec_mode: 'cluster',

// Fork Mode
instances: 1,
exec_mode: 'fork',

部署到 Railway(最簡單)

Railway 是 Heroku 的最佳替代品,適合快速部署 Express API。

步驟一:準備 Procfile

web: node src/index.js

或使用 PM2:

web: npx pm2-runtime ecosystem.config.js --env production

步驟二:連接 GitHub 部署

  1. 前往 railway.app
  2. 新增專案 → Deploy from GitHub
  3. 選擇你的 Repository
  4. Railway 自動偵測並部署

步驟三:設定環境變數

在 Railway Dashboard 的 Variables 區塊:

NODE_ENV=production
ALLOWED_ORIGINS=https://your-frontend.com
DATABASE_URL=postgresql://...

步驟四:新增 PostgreSQL(可選)

點擊「+ New」→「Database」→「PostgreSQL」,Railway 會自動注入 DATABASE_URL


部署到 Render

Render 提供免費方案,適合 Side Project。

步驟一:建立 render.yaml

services:
  - type: web
    name: my-express-api
    env: node
    buildCommand: npm install
    startCommand: node src/index.js
    envVars:
      - key: NODE_ENV
        value: production
    healthCheckPath: /health

步驟二:連接 GitHub

  1. 前往 render.com
  2. 新增 Web Service
  3. 連接 GitHub Repository
  4. 選擇 Node.js 環境

注意事項

  • 免費方案會在閒置 15 分鐘後休眠
  • 首次請求會有冷啟動延遲(約 30 秒)

部署到 AWS EC2 + Nginx

對於需要完全掌控的生產環境,EC2 + Nginx + PM2 是經典組合。

步驟一:EC2 設定

# 連線到 EC2
ssh -i your-key.pem ec2-user@your-ec2-ip

# 安裝 Node.js
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo yum install -y nodejs

# 安裝 PM2
sudo npm install -g pm2

# Clone 專案
git clone https://github.com/your-repo/my-express-api.git
cd my-express-api
npm ci --only=production

# 啟動
pm2 start ecosystem.config.js --env production
pm2 startup
pm2 save

步驟二:Nginx 反向代理

# /etc/nginx/conf.d/my-express-api.conf
upstream express_backend {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 80;
    server_name your-domain.com;

    # 強制 HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL 設定
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    # 安全標頭
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    location / {
        proxy_pass http://express_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 60s;
    }

    # 靜態檔案快取(如果有的話)
    location /static/ {
        alias /var/www/my-express-api/public/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

步驟三:SSL 憑證

# 安裝 Certbot
sudo yum install certbot python3-certbot-nginx -y

# 申請憑證
sudo certbot --nginx -d your-domain.com

# 自動續約(已自動設定)
sudo certbot renew --dry-run

💡 PM2 + Nginx 設定太複雜?

從 Cluster Mode 到 Nginx 負載平衡,設定細節容易出錯。

預約免費諮詢,讓專家幫你設定生產環境。


Vercel 部署:限制與注意事項

vercel-serverless-limitation

插圖:Vercel Serverless 限制說明圖

場景描述:
Technical illustration showing Vercel Serverless 限制說明圖. Clean, professional diagram style.

視覺重點:
- Clear presentation of the concept
- Professional technical diagram style
- Easy to understand visual elements

必須出現的元素:
- Relevant technical components
- Clear labels and annotations
- Logical flow or structure

需要顯示的中文字:

風格:
Clean flat design, technical illustration, modern infographic style

顏色調性:
Professional blues and grays, with accent colors for emphasis

避免元素:
Cluttered design, realistic photos, complex 3D rendering

Vercel 雖然方便,但對 Express 有一些重要限制。

Vercel 的運作方式

Vercel 將 Express 應用轉換為 Serverless Functions:
- 每個請求啟動一個新的函數實例
- 執行完畢後實例可能被銷毀
- 沒有持久的記憶體或連線

不適合 Vercel 的情況

情況 原因
WebSocket Serverless 不支援持久連線
長時間處理 執行時間限制(10-60 秒)
記憶體快取 實例間不共享記憶體
定時任務 沒有 cron job 支援
檔案上傳 無持久檔案系統

如果仍要用 Vercel

建立 vercel.json

{
  "version": 2,
  "builds": [
    {
      "src": "src/index.js",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "src/index.js"
    }
  ]
}

修改 src/index.js

// 匯出 app 供 Vercel 使用
const app = require('./app');

// 本地開發時啟動伺服器
if (process.env.NODE_ENV !== 'production') {
  const PORT = process.env.PORT || 3000;
  app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
  });
}

module.exports = app;

建議:如果你的 Express 專案有上述需求,請使用 Railway 或 Render 而非 Vercel。


Docker 容器化

使用 Docker 可以確保開發與生產環境一致。

Dockerfile

FROM node:20-alpine

# 設定工作目錄
WORKDIR /app

# 安裝 PM2
RUN npm install -g pm2

# 複製 package.json
COPY package*.json ./

# 安裝相依套件
RUN npm ci --only=production

# 複製原始碼
COPY . .

# 建立日誌目錄
RUN mkdir -p logs

# 建立非 root 使用者
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
RUN chown -R appuser:appgroup /app
USER appuser

# 開放 port
EXPOSE 3000

# 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

# 使用 PM2 啟動
CMD ["pm2-runtime", "ecosystem.config.js", "--env", "production"]

docker-compose.yml

version: '3.8'

services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://postgres:password@db:5432/myapp
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:

想了解更多 Docker 部署細節,請參考 Docker Deploy 教學


部署選項比較

express-deployment-options

插圖:Express 部署選項比較圖

場景描述:
Technical illustration showing Express 部署選項比較圖. Clean, professional diagram style.

視覺重點:
- Clear presentation of the concept
- Professional technical diagram style
- Easy to understand visual elements

必須出現的元素:
- Relevant technical components
- Clear labels and annotations
- Logical flow or structure

需要顯示的中文字:

風格:
Clean flat design, technical illustration, modern infographic style

顏色調性:
Professional blues and grays, with accent colors for emphasis

避免元素:
Cluttered design, realistic photos, complex 3D rendering

平台 難度 費用 WebSocket 適用場景
Railway $5/月起 最推薦,簡單又完整
Render 免費起 預算有限
AWS EC2 ⭐⭐⭐⭐ $5/月起 需要完全掌控
Vercel 免費起 簡單 API,有限制
DigitalOcean App ⭐⭐ $5/月起 價格透明

推薦選擇

  • 快速上線 API → Railway
  • 預算有限 → Render(注意冷啟動)
  • 需要 WebSocket → Railway 或 EC2
  • 企業級、高可用 → EC2 + PM2 + Nginx 或 Kubernetes

常見錯誤排解

錯誤一:CORS 錯誤

症狀:瀏覽器顯示 CORS policy 錯誤

解決

app.use(cors({
  origin: ['https://your-frontend.com', 'http://localhost:3000'],
  credentials: true
}));

錯誤二:Cannot find module

症狀:部署後找不到模組

解決
1. 確認 package.json 的 dependencies 正確
2. 不要把模組放在 devDependencies
3. 執行 npm ci 而非 npm install

錯誤三:Port 已被使用

症狀:EADDRINUSE 錯誤

解決

const PORT = process.env.PORT || 3000;

雲端平台會透過 PORT 環境變數指定 port。

錯誤四:記憶體洩漏

症狀:記憶體持續增加,最終 crash

解決
1. PM2 設定 max_memory_restart
2. 檢查未關閉的資料庫連線
3. 檢查未清理的 setInterval

錯誤五:連線逾時

症狀:請求等待過久後逾時

解決
1. 檢查資料庫查詢效能
2. 增加 Nginx 的 proxy_read_timeout
3. 使用連線池


FAQ 常見問題

Q1: Express 需要 PM2 嗎?

生產環境強烈建議使用 PM2,原因:
- 自動重啟 crash 的程序
- Cluster Mode 利用多核心
- 日誌管理
- 零停機更新

Q2: 如何實現零停機部署?

使用 PM2 的 reload 命令:

pm2 reload my-express-api

PM2 會逐一重啟 worker,確保隨時都有可用的實例。

Q3: Express 和 Fastify 哪個效能好?

Fastify 在基準測試中通常比 Express 快 2-3 倍。但 Express 的生態系統更成熟。選擇建議:
- 追求效能 → Fastify
- 追求生態系統、教學資源 → Express

Q4: 如何處理靜態檔案?

生產環境建議:
1. 使用 CDN(CloudFlare、AWS CloudFront)
2. 或讓 Nginx 處理靜態檔案
3. Express 只處理 API

Q5: 需要 Nginx 嗎?

如果使用 Railway、Render 等 PaaS,不需要。
如果使用 EC2 等 IaaS,建議加上 Nginx:
- SSL 終止
- 負載平衡
- 靜態檔案服務
- 額外的安全層


結語

這篇教學涵蓋了 Express.js 從開發到生產的完整部署流程:

  • 生產環境中間件:Helmet、CORS、Rate Limiting、Compression
  • PM2 程序管理:Cluster Mode、自動重啟、零停機更新
  • 部署平台:Railway(最推薦)、Render、AWS EC2
  • Vercel 限制:不支援 WebSocket、有執行時間限制
  • Docker 容器化:確保環境一致性

Express 雖然輕量,但正確的生產環境設定需要加入許多中間件與工具。選擇合適的部署平台,配合 PM2 管理程序,就能讓你的 Express API 穩定運行。

如果你想了解更多 Node.js 部署選項,可以閱讀 Node.js 部署教學


🚀 Express.js 部署需要協助?

從安全性設定到效能調校,Express 生產部署有許多細節需要注意。

預約免費諮詢,讓 VibeFix 的工程師幫你把 Express API 順利上線!

分享文章:
V

VibeFix

專門解決 AI Vibe Coding 後的疑難雜症,讓你的專案順利上線。

這篇文章有幫到你嗎?

如果還有問題,讓我們直接幫你解決!

聯繫我們