2026 年 React 部署:不可变构建、Traefik 与零停机发布
一篇面向生产环境的最新后续文章,将厂商文档转化为运行控制、迁移决策和可验证的发布标准。
2026 年发生了哪些变化
我们重新审视这一主题,是因为系统的运行边界已经发生变化。长期有效的原则依然值得保留,但在当前版本下,一些过去的捷径已经无法覆盖当前需求,甚至可能带来风险。本文以截至 2026 年 7 月 14 日可获得的厂商文档为起点,区分客观事实与本地决策,并将每一次配置调整视为受控的生产变更。
当前一手资料
如何使用本次更新
首先阅读一手资料,记录实际部署的版本,并在修改设置前定义可观测的目标结果。应在具有代表性的环境中测试最小且可撤销的改动。命令执行成功并不等于验收通过;服务健康、数据完整性、延迟、安全边界和回滚所需时间才是标准。旧文中仍然有效的诊断顺序会作为运行基础保留在下文,但所有与版本有关的示例都必须对照当前文档复核。下文提到的配置文件和命令见原始指南。
运行基础
在本地构建 React 应用并不难,但要正确部署到生产服务器,多数开发者都会遇到意想不到的障碍。本指南记录了一次真实的部署调试过程,在这个过程中一切看起来都配置正确——容器正常运行、Traefik 标签已设置、DNS 解析正常——但应用程序持续返回 404 错误。
今天,我们将介绍在本地构建 React 应用程序并将其部署到生产 Docker 服务器的完整过程,包括正确的反向代理配置、自动化 SSL 证书管理和专业的域名路由。这种方法建立在我们自托管解决方案的理念之上——正如我们此前介绍的,通过自托管 n8n 实现工作流自动化,以及构建多租户开发栈来全面掌控运行环境。
传统 React 部署的问题
大多数 React 部署教程跳过了关键的生产细节。您会找到展示 npm run build 和将文件复制到 nginx 的指南,但它们很少涵盖:
配置冲突:
- 覆盖全局重定向的自定义 HTTP 路由器
- 导致静默失败的 Traefik 标签语法错误
- 健康检查中的 IPv6 与 IPv4 绑定问题
- 服务端口映射缺失,导致 404 错误
资源管理:
- 磁盘已满导致容器注册失败
- 不必要的构建产物造成 Docker 镜像膨胀
- 低效的缓存策略拖慢部署
- 内存限制影响构建性能
生产环境准备:
- 正确实现 SSL 证书自动化
- 零停机部署策略
- 健康检查配置
- 日志记录和监控集成
结果是,即使“一切看起来都正确”,仍要花费数小时排查:为何本地运行正常的应用,到了生产环境却返回难以解释的 404 错误。
所用工具
先看看这套精简的 React 部署架构中,各个组件分别承担什么职责:
Vite:现代构建工具
Vite 提供快速的开发体验和经过优化的生产构建。与 Create React App 不同,Vite 在开发阶段使用原生 ES 模块,并为生产环境打包出高度优化的产物。React 应用的构建只需数秒,而不是数分钟。
关键优势在于,Vite 自动处理代码分割、无用代码消除(tree shaking)和资源优化。无需复杂的 webpack 配置,就能获得可用于生产环境的构建产物。
Docker:通过容器化保持一致
Docker 确保 React 应用在开发和生产环境中以相同方式运行。本地提供应用服务的 nginx 容器,在生产服务器上也会表现一致,从而消除典型的“在我的机器上能运行”问题。
可以把 Docker 理解为:将整个应用环境,包括 React 构建文件、nginx 配置和运行时,打包进可移植、能在任何地方运行的容器。
Traefik:智能反向代理
Traefik 负责智能分发流量,根据域名自动将请求路由到正确的容器化应用。无需为每个新应用手动配置复杂的 nginx 或 Apache 规则,Traefik 会读取 Docker 容器标签并自动设置路由。
在多租户 Docker 部署方案中,我们已经展示了 Traefik 管理多个客户环境的能力。同样的原则也适用于在单台服务器上管理多个 React 应用。
Traefik 的优势在于,只需少量配置,就能通过 Let’s Encrypt 自动处理 SSL 终止、自动发现服务,并提供详细监控。
nginx:生产环境 Web 服务器
nginx 能以出色的性能提供 React 静态构建文件。它是生产环境静态内容服务的事实标准,以极少的资源高效处理数千个并发连接。
理解部署流程
从本地开发到生产环境的完整流程如下:
- 本地开发:使用 npm run dev 开发并测试 React 应用
- 生产构建:使用 npm run build 生成优化后的静态文件
- 容器化:将构建文件打包进 nginx Docker 容器
- 服务器部署:上传容器并在生产服务器上启动
- Traefik 注册:自动配置路由并签发 SSL 证书
- 健康监控:通过持续健康检查确保可用性
这套流程的优势在于自动化。正确配置后,只需一条命令,就能在不到 60 秒内部署更新。
配置 React 应用
面向生产环境的项目结构
组织 React 项目时,应考虑部署需求:
my-react-app/
├── src/ # React source code
├── public/ # Static assets
├── dist/ # Build output (auto-generated)
├── package.json # Dependencies
├── package-lock.json # Locked dependency versions (npm ci)
├── vite.config.ts # Vite configuration
├── Dockerfile # Container definition
├── nginx.conf # nginx configuration
└── docker-compose.yml # Deployment definition
优化 Vite 配置
创建 vite.config.ts,并采用针对生产环境优化的设置:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist',
sourcemap: false, // Do not publish source maps in production
// Minification is on by default (Oxc minifier in Vite 8)
rolldownOptions: {
output: {
// Vite 8 (Rolldown): codeSplitting replaces the manualChunks object form
codeSplitting: {
groups: [
{ test: /node_modules[\\/](react|react-dom|scheduler)[\\/]/, name: 'vendor' },
],
},
},
},
},
server: {
port: 3000,
host: true, // Enable network access
},
})
此配置会:
- 分离第三方库,改善缓存效果
- 压缩代码,减小文件体积
- 在生产环境中禁用 source map,防止代码暴露
- 优化分块,提高加载速度
构建生产版本
构建经过优化的生产包:
# Install the exact dependency versions from package-lock.json
npm ci
# Create production build
npm run build
# Verify build output
ls -lh dist/
dist/ 文件夹应包含:
- index.html:入口文件
- assets/:压缩后的 JS、CSS 和图像
- 来自 public/ 的静态文件
创建生产容器
用于 React 的 nginx 配置
React 应用使用客户端路由,因此需要专门的 nginx 配置。创建 nginx.conf:
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Gzip compression for better performance
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/x-javascript application/xml+rss
application/javascript application/json;
# Security headers (a location with its own add_header drops these)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# SPA: Route all paths to index.html for client-side routing
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets aggressively
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Revalidate HTML on every request so new releases show up immediately
location ~* \.html$ {
expires -1;
}
# Health check endpoint for monitoring
location /health {
access_log off;
default_type text/plain;
return 200 "healthy\n";
}
}
关键是 try_files $uri $uri/ /index.html,它确保 React Router 在生产环境中正常工作:所有路由都由主 index.html 文件提供页面。
用于生产环境的 Dockerfile
创建经过优化的 Dockerfile:
FROM nginx:alpine
# Copy custom nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copy production build files
COPY dist/ /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
这里使用 nginx:alpine 构建精简的生产镜像,截至 2026 年 10 月压缩后大小不足 30MB,却包含提供 React 应用服务所需的全部内容。
Docker Compose 配置
创建 docker-compose.yml,方便部署:
services:
my-react-app:
build:
context: .
dockerfile: Dockerfile
container_name: my-react-app
restart: unless-stopped
networks:
- proxy
labels:
# Enable Traefik
- "traefik.enable=true"
# Define routing rule
- "traefik.http.routers.myapp.rule=Host(`app.yourdomain.com`)"
- "traefik.http.routers.myapp.entrypoints=https"
- "traefik.http.routers.myapp.tls=true"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
# Define service port
- "traefik.http.services.myapp.loadbalancer.server.port=80"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:80/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 5s
networks:
proxy:
external: true
关键配置说明:
labels 部分是许多部署失败的原因。请注意,以下配置不包含在内:
- 不单独定义 HTTP 路由器
- 不添加自定义重定向中间件
- 不配置 HTTP 入口点
原因是 Traefik 的全局配置已处理 HTTP→HTTPS 重定向。在我们的配置中,添加自定义 HTTP 路由器干扰了这一行为,并导致了 404 错误,这正是我们在调试过程中解决的问题。
部署到生产服务器
服务器前置条件
生产服务器需要具备:
Docker 环境:
# Verify Docker is installed
docker --version
docker compose version
# Verify Traefik is running
docker ps | grep traefik
# Verify proxy network exists
docker network ls | grep proxy
如果尚未配置 Traefik,请参阅我们的多租户 Docker 指南,其中介绍了完整的 Traefik 设置流程。
足够的磁盘空间:
# Check available space
df -h /
# You need at least 2-5GB free for Docker operations
DNS 配置:
- 将 app.yourdomain.com 指向服务器的 IP 地址
- 等待 DNS 传播,通常需要 5-60 分钟
上传应用
将应用传输到服务器:
# From your local machine
scp -r my-react-app/ user@your-server:/opt/
# Or use rsync for efficient updates
rsync -avz --exclude 'node_modules' \
my-react-app/ user@your-server:/opt/my-react-app/
构建并启动容器
通过 SSH 登录服务器并部署:
# Navigate to application directory
cd /opt/my-react-app
# Build the Docker image
docker compose build
# Start the container
docker compose up -d
# Verify it's running
docker compose ps
验证部署
检查各项功能是否正常:
# Test container health internally
docker compose exec my-react-app wget -q -O- http://127.0.0.1/health
# Check Traefik routing (wait 30 seconds for SSL)
curl -I https://app.yourdomain.com
# View container logs
docker compose logs -f
curl 命令应返回 HTTP/2 200,表示请求成功。
常见部署故障及解决方法
配置正确却仍返回 404
症状:容器内部工作正常,但 Traefik 返回 HTTP/2 404。
根因:同一服务定义了多个路由器,却没有正确映射服务端口;或自定义 HTTP 路由器覆盖了 Traefik 的全局重定向。
解决方法:
# Remove these labels if present:
# - "traefik.http.middlewares.myapp-redirect.redirectscheme.scheme=https"
# - "traefik.http.routers.myapp-http.rule=Host(`app.yourdomain.com`)"
# - "traefik.http.routers.myapp-http.entrypoints=http"
# - "traefik.http.routers.myapp-http.middlewares=myapp-redirect"
# Keep only HTTPS router:
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`app.yourdomain.com`)"
- "traefik.http.routers.myapp.entrypoints=https"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
- "traefik.http.services.myapp.loadbalancer.server.port=80"
Traefik 的全局 HTTP→HTTPS 重定向在 traefik.yml 中配置,会自动处理 HTTP 流量。为各服务自定义的 HTTP 路由器可能与该重定向发生冲突。
容器状态为 unhealthy
症状:docker compose ps 将容器显示为“unhealthy”。
根因:健康检查使用的 localhost 解析为 IPv6 地址 [::1],但 nginx 只监听 IPv4。
解决方法:
healthcheck:
# Use explicit IPv4 address instead of localhost
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:80/health"]
Docker 构建因“No Space Left”失败
症状:构建因磁盘空间错误而失败。
解决方法:
# Check disk usage
df -h /
# Clean Docker system
docker system prune -a -f
# Remove unused images
docker image prune -a -f
# Remove unused volumes
docker volume prune -f
如果磁盘确实已接近满容量(>95%),需要释放空间或扩容。Docker 的镜像层缓存和构建操作需要临时空间。
SSL 证书未生成
症状:10+ 分钟后,curl 仍显示自签名证书。
常见原因:
- DNS 未正确指向服务器
- 互联网无法访问 80/443 端口
- 触发 Let’s Encrypt 速率限制,例如,每组完全相同的主机名每 7 天最多可签发 5 张证书,或每个注册域名每 7 天最多可签发 50 张证书
解决方法:
# Verify DNS resolution
dig app.yourdomain.com
# Test port accessibility
curl -I http://app.yourdomain.com
# Check Traefik logs for ACME errors
docker logs traefik 2>&1 | grep -i acme
# Restart Traefik if needed
docker restart traefik
刷新页面时 React Router 返回 404
症状:应用首次加载正常,但刷新 /about 等路由时出现 404。
根因:nginx 配置缺少 try_files 指令。
解决方法:确保 nginx.conf 包含以下配置:
location / {
try_files $uri $uri/ /index.html;
}
这会让 nginx 为所有路由提供 index.html,由 React Router 在客户端处理路由。
容器已启动,但 Traefik 无法访问
症状:容器正在运行,但 Traefik 返回“Service Unavailable”。
解决方法:
# Verify container is on correct network
docker network inspect proxy
# Check if container is listed
docker inspect my-react-app | grep -A 20 Networks
# Ensure proxy network exists
docker network create proxy
生产环境优化
实现零停机部署
以尽量短的停机时间更新应用。使用 docker compose up 重新创建单个容器仍会短暂中断流量;要实现真正的零停机,必须在旧实例停止前,让 Traefik 后方的第二个健康实例投入运行:
#!/bin/bash
# deploy-update.sh - rebuild and replace the container
# Recreating a single container briefly interrupts traffic (see above)
set -euo pipefail
cd /opt/my-react-app
# Pull latest code (from git or updated files)
git pull origin main
# Build new production assets from the lockfile
npm ci
npm run build
# Build the new image while the old container keeps serving
docker compose build my-react-app
# Replace the container: Compose stops the old one, then starts the new one,
# and --wait returns once the new container reports healthy
docker compose up -d --no-deps --wait my-react-app
高级健康检查
实施全面的健康监控:
healthcheck:
test: |
wget --no-verbose --tries=1 --spider http://127.0.0.1:80/health &&
wget --no-verbose --tries=1 --spider http://127.0.0.1:80/
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
此检查同时覆盖健康端点和主应用路由,确保整个应用都能正常响应。
性能优化
微调 nginx,提升性能:
# Add to nginx.conf
server {
# ... existing config ...
# Increase buffer sizes for large headers
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
# Enable keep-alive connections
keepalive_timeout 65;
# Optimize file serving
sendfile on;
tcp_nopush on;
tcp_nodelay on;
}
资源限制
通过容器限制防止资源耗尽:
services:
my-react-app:
# ... existing config ...
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
使用 nginx 提供 React 静态文件只需很少资源:512MB 内存和半个 CPU 核是合理的初始资源上限;应通过负载测试确认能否满足实际流量所需的并发量。
自托管 Docker 部署为何重要
在 Docker 基础设施上自托管 React 应用,让您全面掌控部署流水线,避免供应商锁定。可以在自己的基础设施上部署不限数量的应用,自定义部署流程的各个方面,并与现有自托管服务顺畅集成。
这套方法尤其适合与我们的多租户 Docker 架构结合使用,可在同一基础设施上托管多个客户的应用,并为每个客户配置独立的容器和网络。
自动化与 CI/CD 集成
通过 GitHub Actions 部署
每次推送后自动部署:
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '24'
cache: 'npm'
- name: Build React app
run: |
npm ci
npm run build
- name: Deploy to server
uses: appleboy/[email protected]
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: "dist/,Dockerfile,nginx.conf,docker-compose.yml"
target: "/opt/my-react-app"
- name: Restart container
uses: appleboy/[email protected]
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/my-react-app
docker compose up -d --build --wait
GitLab CI/CD 流水线
对于 GitLab 用户:
# .gitlab-ci.yml
stages:
- build
- deploy
build:
stage: build
image: node:24
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
deploy:
stage: deploy
image: alpine:3.23
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
# Trust the server's host key (store `ssh-keyscan your-server` output in SSH_KNOWN_HOSTS)
- mkdir -p ~/.ssh && echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
script:
- scp -r dist/ Dockerfile nginx.conf docker-compose.yml $SERVER_USER@$SERVER_HOST:/opt/my-react-app/
- ssh $SERVER_USER@$SERVER_HOST "cd /opt/my-react-app && docker compose up -d --build --wait"
rules:
- if: $CI_COMMIT_BRANCH == "main"
监控生产部署
日志策略
实施全面的日志记录:
# Add to docker-compose.yml
services:
my-react-app:
# ... existing config ...
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
高效查看日志:
# Real-time logs
docker compose logs -f my-react-app
# Last 100 lines
docker compose logs --tail=100 my-react-app
# Logs from specific time
docker compose logs --since=2h my-react-app
# Filter for errors only
docker compose logs my-react-app | grep -i error
指标与告警
监控容器健康状态:
#!/bin/bash
# health-check.sh - Regular health monitoring
CONTAINER="my-react-app"
WEBHOOK_URL="your-notification-webhook"
STATUS=$(docker inspect --format='{{.State.Health.Status}}' $CONTAINER)
if [ "$STATUS" != "healthy" ]; then
curl -X POST $WEBHOOK_URL \
-H 'Content-Type: application/json' \
-d "{\"text\":\"⚠️ Container $CONTAINER is $STATUS\"}"
fi
通过 cron 每 5 分钟运行一次,即可实现基本监控。
将 React 连接到后端基础设施
React 应用通常需要与后端服务通信,这可以自然地融入自托管基础设施。如果使用 n8n 进行工作流自动化,或使用 Windmill 执行后端工作流,请在 nginx 配置中正确设置 CORS 和 API 路由:
# Add to nginx.conf, above the server block: set Connection only for WebSocket upgrades
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Inside the server block. Same-origin proxying: the browser makes no
# cross-origin request, so these API calls need no CORS headers
location /api {
proxy_pass http://your-backend-service:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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;
}
当所有服务都处于同一 Docker 网络时,这种集成可以顺畅运行,我们的多租户架构指南展示了这一点。
针对不同环境的构建
不同环境往往需要不同配置:
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig(({ mode }) => ({
plugins: [react()],
define: {
'import.meta.env.VITE_API_URL': JSON.stringify(
mode === 'production'
? 'https://api.yourdomain.com'
: 'http://localhost:3000'
),
},
build: {
outDir: 'dist',
sourcemap: mode !== 'production',
},
}))
为不同环境执行构建:
# Development build
npm run build -- --mode development
# Staging build
npm run build -- --mode staging
# Production build
npm run build -- --mode production
理解这套部署方案的实际价值
如果您正在做以下工作,这套部署方法就很有用:
管理多个应用:
- 在同一基础设施上部署 React 应用和后端服务
- 在所有项目中采用一致的部署流程
- 与 n8n、Windmill 等自托管工具集成
为客户构建应用:
- 使用 SSL 保护的专业自定义域名
- 全面掌控基础设施和部署
- 不受平台限制或供应商锁定影响
学习基础设施:
- 理解 Docker 容器化基础
- 掌握 Traefik 反向代理配置
- 系统地排查生产部署问题
本文记录的部署方案来自一次真实调试:描述的问题确实发生过,解决方法也确实有效。因此,它比理论教程更有价值,能让您了解实际陷阱及规避方法。
结合我们的多租户 Docker 架构,这套方案为可扩展的自托管应用交付奠定基础,并让您全面掌控交付过程。
相关资源
更多自托管基础设施指南,请参阅以下资源:
- 自托管 n8n 实现工作流自动化:自动化部署和基础设施任务
- 使用 Docker 自托管 Windmill:另一种工作流自动化平台
- 构建多租户 Docker 技术栈:面向多应用的完整 Traefik 配置
- tva Duplicate Pro:用于内容工作流的 WordPress 自动化工具
这些指南从不同角度介绍了如何构建自托管基础设施,在保持专业标准的同时让您全面掌控系统。
获取专业支持
建立可用于生产环境的 React 部署方案,需要考虑许多基础设施因素。虽然我们提供了完整文档,但每个项目都有独特需求、现有基础设施约束和具体性能要求。
如果您正在搭建用于生产环境的 React 部署基础设施,或需要针对客户交付需求进行定制,我们可以协助:
- 按您的工作流定制部署流水线
- 与现有 CI/CD 系统集成
- 优化高流量应用的性能
- 制定多区域部署策略
- 为团队提供 Docker 和 Traefik 最佳实践培训
- 持续管理和监控基础设施
请通过 tva.sg/contact 联系我们,讨论 React 部署需求,获取专业的实施指导。
从配置调整到运行决策
真正需要确认的,不只是平台能否完成配置,还包括团队能否说明责任归属、发现配置漂移、无需临时摸索即可恢复服务,并证明变更改善了预期结果。我们为每项配置变更配套负责人、基线、回滚路径和复核窗口,使一次性修复成为可持续的运营能力。生产变更记录应包含原因、已审查证据、准确范围,以及有权停止或撤销变更的人员。发布后,将约定指标与基线比较,并把结果保存在变更记录中。若没有预期改善,回滚是合理结果,而非失败;这种纪律比任何单项配置值更重要。同一记录还为下一位运维人员提供可信起点,使后续优化成为有测量依据的决策,而非新一轮猜测。