Reactアプリケーションを本番環境にデプロイする:TraefikリバースプロキシによるDocker完全セットアップガイド
Reactアプリケーションをローカルで構築するのは簡単です。しかし、本番サーバーに正しくデプロイするとなると、多くの開発者が予期しない壁にぶつかります。このガイドでは、すべてが正しく設定されているように見える—コンテナは稼働中、Traefikのラベルも設定済み、DNSも解決済み—にもかかわらず、アプリケーションが繰り返し404エラーを返した実際のデプロイデバッグセッションを記録しています。
今回は、Reactアプリケーションをローカルでビルドし、適切なリバースプロキシ設定、自動SSL、プロフェッショナルなドメインルーティングを備えた本番Dockerサーバーにデプロイする完全なプロセスをご紹介します。このアプローチは、セルフホスティングソリューションの理念に基づいています。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証明書のプロビジョニングが行われます
- ヘルスモニタリング: 継続的なヘルスチェックが可用性を確保します
このアプローチの強みは自動化にあります。正しく設定すれば、1つのコマンドで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を使用することで、Reactアプリの配信に必要なすべてを含む最小限の本番イメージ(わずか約8MB)が得られます。
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
重要な設定上の注意点:
ラベルセクションは、多くのデプロイが失敗するポイントです。ここで含めていないものに注目してください。
- 個別の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がセットアップされていない場合は、包括的なTraefikセットアップをカバーしているマルチテナントDockerガイドを参照してください。
十分なディスク容量:
# 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のレート制限に達している(1ドメインあたり週5回)
解決策:
# 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 - Zero-downtime deployment
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 new Docker image
docker compose build
# Start new container (old one still running)
docker compose up -d --no-deps --build my-react-app
# Traefik automatically routes to healthy container
# Old container is automatically stopped after new one is healthy
高度なヘルスチェック
包括的なヘルスモニタリングを実装します。
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
静的なReactファイルを配信するnginxに必要なリソースは最小限です—512MBのメモリとCPUコア半分で、数千の同時ユーザーに対応できます。
セルフホストDockerデプロイが重要な理由
ReactアプリケーションをDockerインフラストラクチャ上でセルフホストすることで、ベンダーロックインなしにデプロイパイプラインを完全にコントロールできます。自社のインフラストラクチャ上に無制限のアプリケーションをデプロイし、デプロイプロセスのあらゆる側面をカスタマイズし、既存のセルフホストサービスとシームレスに統合できます。
このアプローチは、マルチテナント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/scp-action@v0.1.4
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/ssh-action@v0.1.10
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設定で適切なCORSとAPIルーティングを構成してください。
# 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
このセットアップを理解する真の価値
このデプロイアプローチは、以下のような方にとって重要です。
複数のアプリケーションを管理する方:
- 同じインフラストラクチャ上でReactアプリとバックエンドサービスを並行してデプロイできます
- すべてのプロジェクトで一貫したデプロイプロセスを使用できます
- n8nやWindmillなどのセルフホストツールと統合できます
クライアント向けに構築する方:
- プロフェッショナルなSSL保護されたカスタムドメイン
- インフラストラクチャとデプロイの完全なコントロール
- プラットフォームの制限やベンダーロックインがありません
インフラを学習する方:
- Dockerコンテナ化の基礎を理解できます
- Traefikリバースプロキシの設定をマスターできます
- 本番デプロイの問題を体系的にデバッグできます
ここで文書化されたセットアップは、実際のデバッグセッションに基づいています。記載された問題は実際に発生し、ここに示された解決策は実際に機能しました。このことは、実際の落とし穴とその回避方法が示されているため、理論的なチュートリアルよりも価値があります。
マルチテナントDockerアーキテクチャと組み合わせることで、完全にコントロール可能なスケーラブルなセルフホスト型アプリケーション配信の基盤を形成します。
関連リソース
セルフホストインフラストラクチャに関するその他のガイドについては、以下のリソースをご確認ください。
- n8nのセルフホストによるワークフロー自動化 – デプロイとインフラタスクの自動化
- DockerによるWindmillのセルフホスト – 代替ワークフロー自動化プラットフォーム
- マルチテナントDockerスタックの構築 – 複数アプリケーション向けの包括的なTraefikセットアップ
- tva Duplicate Pro – コンテンツワークフロー向けWordPress自動化ツール
これらのガイドは、プロフェッショナルな基準を維持しながら完全なコントロールを提供するセルフホストインフラの構築に関する、さまざまな側面を実証しています。
プロフェッショナルサポートのご案内
本番グレードのReactデプロイ環境の構築には、多くのインフラに関する考慮事項が伴います。包括的なドキュメントを提供していますが、プロジェクトごとに固有の要件、既存のインフラ制約、特定のパフォーマンスニーズがあります。
本番環境向けのReactデプロイインフラを導入される場合や、特定のクライアントデリバリーニーズに合わせたカスタマイズが必要な場合は、以下のサポートを提供いたします。
- ワークフローに合わせたカスタムデプロイパイプライン
- 既存のCI/CDシステムとの統合
- 高トラフィックアプリケーション向けのパフォーマンス最適化
- マルチリージョンデプロイ戦略
- DockerとTraefikのベストプラクティスに関するチームトレーニング
- 継続的なインフラ管理とモニタリング
Reactデプロイのニーズについてご相談いただき、導入に関するプロフェッショナルなガイダンスをご希望の場合は、tva.sg/contactからお問い合わせください。
既存のエージェンシーの拡大、新しいSaaS製品の立ち上げ、エンタープライズグレードのクライアントデリバリー能力の構築のいずれであっても、独立性を維持しながらプロフェッショナルな成果を提供するセルフホスト型コンテナ化Reactデプロイの実現を支援いたします。