将 React 应用部署到生产环境:Docker 与 Traefik 反向代理完整配置
在本地构建 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 自动处理代码分割、摇树优化和资源优化,无需复杂的 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
├── 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, // Disable in production for security
minify: 'terser',
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
},
},
},
},
server: {
port: 3000,
host: true, // Enable network access
},
})
这个配置会:
- 分离第三方库,改善缓存效果
- 压缩代码,减小文件体积
- 在生产环境中禁用源映射,避免发布原始、未经压缩的源代码
- 优化代码块拆分,加快加载速度
构建生产版本
构建优化后的生产包:
# Install dependencies
npm install
# 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
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";
}
# No cache for HTML to ensure updates are immediate
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
}
# Health check endpoint for monitoring
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
关键是 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 月,其在 Docker Hub 上的压缩大小约为 29 MB,并包含提供 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 路由器造成了冲突。
容器显示不健康状态
现象: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 张重复证书,或每个注册域名最多签发 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 | 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
生产环境优化
实现短暂停机部署
仅需短暂中断即可更新应用:
#!/bin/bash
# deploy-update.sh - Low-downtime deployment
set -e
cd /opt/my-react-app
# Pull latest code (from git or updated files)
git pull origin main
# Build new production assets
npm install
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
docker compose up -d --no-deps my-react-app
# Traefik routes to the new container once its health check passes
如脚本注释所述,docker compose up 会先停止旧容器,再启动替代容器,而使用固定 container_name 的服务无法并行运行两个实例,因此 nginx 启动期间会有短暂的服务中断。真正的零停机更新需要在 Traefik 后面运行第二个副本,或采用蓝绿部署。
高级健康检查
实施全面的健康监控:
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;
keepalive_requests 100;
# 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 静态文件所需的资源很少,因此 512 MB 内存和半个 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@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
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
GitLab CI/CD 流水线
GitLab 用户可以使用以下配置:
# .gitlab-ci.yml
stages:
- build
- deploy
build:
stage: build
image: node:18
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
deploy:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
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"
only:
- 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 配置中设置 API 路由。如下所示,通过同源代理转发 /api 可以避免跨源请求,因此浏览器进行这些调用时无需 CORS 响应头:
# Add to nginx.conf for API proxying
location /api {
proxy_pass http://your-backend-service:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
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
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
理解这套配置的实际价值
如果您正在做以下工作,这种部署方法就很有用:
管理多个应用:
为客户开发:
- 提供通过 SSL 保护的专业自定义域名
- 完全掌控基础设施和部署
- 不受平台限制,避免供应商锁定
学习基础设施:
- 理解 Docker 容器化的基础知识
- 掌握 Traefik 反向代理配置
- 系统地排查生产部署问题
这里记录的配置来自一次真实的调试过程,所描述的问题确实发生过,解决方法也确实奏效。相比理论教程,它的价值在于展示了实际陷阱及其规避方法。
结合我们的多租户 Docker 架构,这套配置为可扩展的自托管应用交付提供了基础,让您完全掌控整个过程。
相关资源
更多自托管基础设施指南,请参阅以下资源:
- 自托管 n8n 进行工作流自动化——自动化部署和基础设施任务
- 使用 Docker 自托管 Windmill——另一种工作流自动化平台
- 构建多租户 Docker 栈——面向多个应用的完整 Traefik 配置
- tva Duplicate Pro——用于内容工作流的 WordPress 自动化工具
这些指南从不同角度展示了如何构建自托管基础设施,在完全掌控的同时保持专业标准。
获取专业支持
配置生产级 React 部署,需要考虑许多基础设施问题。虽然我们提供了详尽文档,但每个项目都有独特的需求、现有基础设施限制和特定性能要求。
如果您正在构建用于生产环境的 React 部署基础设施,或需要根据客户交付要求进行定制,我们可以协助:
- 根据您的工作流定制部署流水线
- 与现有 CI/CD 系统集成
- 优化高流量应用的性能
- 制定多区域部署策略
- 为团队提供 Docker 和 Traefik 最佳实践培训
- 持续管理和监控基础设施
通过 tva.sg/contact 联系我们,讨论您的 React 部署需求,并获得专业的实施指导。
无论您正在扩大现有代理服务商的业务、推出新的 SaaS 产品,还是建立企业级客户交付能力,我们都可以帮助您采用自托管、容器化的 React 部署,在保持独立性的同时提供专业成果。