📄
概念 📚 beginner-stepup

CloudFront + S3 + ALB で公開する(Month 5 Week 20)

S3 に React ビルド成果物を配置し、ALB 経由で Hono API に接続し、CloudFront が 1 つのドメイン配下で両方を配信する構成の作り方

Week 20 で、ここまで作ったリソース(S3 に React SPA、ALB 経由の Hono API、RDS)を 1 本の URL に束ねて公開します。CloudFront の複数オリジン機能を使うことで、/api/* は ALB に、それ以外は S3 に振り分けます。

これが完成すると https://xxx.cloudfront.net にアクセスするだけで、SNS clone が動きます。Menta 契約の最終目標の 1 つです。


最終構成

             [ユーザー]
                │ https://xxx.cloudfront.net

         ┌──────────────┐
         │  CloudFront  │  ← エッジキャッシュ + HTTPS
         └───┬──────┬───┘
             │      │
       /*    │      │   /api/*
             ↓      ↓
         ┌───┐    ┌───┐
         │ S3│    │ALB│
         │web│    └─┬─┘
         └───┘      │

              ┌──────────┐
              │ App EC2  │
              │ (Private)│
              └────┬─────┘


              ┌──────────┐
              │   RDS    │
              └──────────┘

Step 1: S3 バケットを作成して Web ビルドを配置

1-1. バケット作成

AWS Console → S3 → Create bucket:

項目
Bucket namekinoshita-sns-clone-web(グローバル一意)
RegionAsia Pacific (Tokyo)
Block Public Accessすべてブロック を ON(CloudFront OAC で配信)
Bucket VersioningDisable(カリキュラム用)

重要: S3 を直接公開せず、CloudFront OAC(Origin Access Control) 経由のアクセスのみに絞ります。

1-2. React を build して sync

ローカル(Git Bash)で:

cd web
pnpm build
# web/dist/ に静的ファイルが生成される

aws s3 sync dist/ s3://kinoshita-sns-clone-web/ \
  --delete \
  --cache-control "public, max-age=31536000, immutable" \
  --exclude "index.html"

# index.html だけはキャッシュ短め(SPA なので新バージョンを即反映)
aws s3 cp dist/index.html s3://kinoshita-sns-clone-web/ \
  --cache-control "public, max-age=60"

--cache-control を分けるのは ハッシュ付き JS/CSS は長期キャッシュindex.html は短期 というベストプラクティス。


Step 2: ALB(Application Load Balancer)を作成

2-1. Target Group(App EC2)

AWS Console → EC2 → Target Groups → Create target group

項目
Target typeInstances
Target group nameapp-tg
ProtocolHTTP
Port3001
VPCカリキュラム用 VPC
Health check path/health
Health check porttraffic port(3001)

Register targets で App EC2 を選び、Port 3001 で追加。

2-2. ALB 本体

EC2 → Load Balancers → Create Application Load Balancer

項目
Load balancer namesns-clone-alb
SchemeInternet-facing
VPCカリキュラム用 VPC
MappingsPublic Subnet × 2(AZ 冗長)
Security groupalb-sg(Inbound: 80/443 from 0.0.0.0/0)
ListenerHTTP:80 → forward → app-tg

重要: app-sg の Inbound に alb-sg からの 3001 を許可しておく。

作成後、ALB の DNS 名(sns-clone-alb-xxx.ap-northeast-1.elb.amazonaws.com)を控える。

2-3. HTTPS を ALB で止めるか、CloudFront で止めるか

本カリキュラムでは CloudFront で HTTPS を終端 し、ALB ↔ CloudFront は HTTP で十分(CloudFront と ALB の間は AWS 内部ネットワーク)。シンプルさ重視。

セキュリティを上げたいなら ALB にも ACM 証明書を当てる選択肢あり。


Step 3: CloudFront Distribution を作成

AWS Console → CloudFront → Create distribution:

3-1. オリジン 1: S3(Web)

項目
Origin domainkinoshita-sns-clone-web.s3.ap-northeast-1.amazonaws.com
Origin accessOrigin Access Control を新規作成(kinoshita-sns-clone-web-oac
Origin typeS3

Origin Access Control を作ると、S3 バケットポリシーを更新するためのコピー可能な JSON が表示されます。それを S3 → Permissions → Bucket policy に貼り付け:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipal",
      "Effect": "Allow",
      "Principal": { "Service": "cloudfront.amazonaws.com" },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::kinoshita-sns-clone-web/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/YOUR_DIST_ID"
        }
      }
    }
  ]
}

3-2. オリジン 2: ALB(API)

既存の Distribution に Origins → Create origin

項目
Origin domainALB の DNS 名
ProtocolHTTP only(CloudFront ↔ ALB を HTTP に)
HTTP port80

3-3. Default Cache Behavior(/* = S3 向け)

項目
Path patternDefault (*)
OriginS3 オリジン
Viewer protocol policyRedirect HTTP to HTTPS
Allowed HTTP methodsGET, HEAD
Cache policyCachingOptimized

3-4. 追加 Behavior(/api/* = ALB 向け)

CloudFront → Behaviors → Create behavior

項目
Path pattern/api/*
OriginALB オリジン
Viewer protocol policyRedirect HTTP to HTTPS
Allowed HTTP methodsGET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Cache policyCachingDisabled(API レスポンスはキャッシュしない)
Origin request policyAllViewer(認証ヘッダを転送)

3-5. SPA のルーティング対応

React Router を使っているので、/posts/abc123 に直接アクセスされたら S3 は 404 を返します。CloudFront で 404 を index.html にフォールバック:

Error pages → Create custom error response

項目
HTTP error code404
Customize error responseYes
Response page path/index.html
HTTP response code200

403 も同様に設定(S3 の OAC で権限不足時は 403 を返すため)。


Step 4: 動作確認

Distribution が Deployed になるまで 10〜15 分。その後:

# CloudFront のドメインにアクセス
curl -I https://xxx.cloudfront.net/
# HTTP/2 200 が返れば S3 オリジンが動いている

curl https://xxx.cloudfront.net/api/health
# {"status":"ok"} が返れば ALB → App EC2 → Hono のチェーンが通っている

ブラウザでアクセスして:

  • トップページが表示される
  • ログイン → 投稿作成 → タイムライン表示 が一通り動く
  • ページを F5 しても 404 にならない(カスタムエラー設定が効いている)

独自ドメインを当てる(任意)

Route 53 でドメインを買うか、既存ドメインの DNS を Route 53 にする:

  1. Route 53 → Hosted zones → 自分のドメイン
  2. ACM(us-east-1 リージョン必須)で証明書を発行:*.example.com + example.com
  3. ACM の DNS 検証レコードを Route 53 に追加
  4. CloudFront の Alternate domain names に app.example.com を追加、証明書を ACM のものに
  5. Route 53 で app.example.com → CloudFront Distribution の Alias レコードを作成

デプロイ更新フロー

次回以降の更新は:

# Web を更新
cd web
pnpm build
aws s3 sync dist/ s3://kinoshita-sns-clone-web/ --delete \
  --cache-control "public, max-age=31536000, immutable" \
  --exclude "index.html"
aws s3 cp dist/index.html s3://kinoshita-sns-clone-web/ \
  --cache-control "public, max-age=60"

# CloudFront のキャッシュを無効化(index.html のみ)
aws cloudfront create-invalidation \
  --distribution-id YOUR_DIST_ID \
  --paths "/index.html"

API は git push origin main → GitHub Actions が自動デプロイ(b19 参照)。


Week 20 のアウトプット

  • ☐ S3 バケットに React ビルド成果物が配置されている
  • ☐ ALB が App EC2 を Target に持ち、Health check が Healthy
  • ☐ CloudFront Distribution が Deployed で、S3 と ALB の 2 オリジン設定
  • /api/* Behavior で Caching 無効 + Origin Request Policy が AllViewer
  • ☐ SPA の 404 フォールバックが設定されている
  • https://xxx.cloudfront.net でアプリが正常に動作する

よくある失敗

  • S3 を Public 公開にしてしまう: OAC を使わないと意味がない。Block Public Access は全 ON
  • /api/* の Cache Policy が CachingOptimized: API レスポンスがキャッシュされ、古いデータが返る。必ず CachingDisabled
  • Origin Request Policy のデフォルトを使う: Authorization ヘッダが ALB に届かず 401 連発。AllViewer or カスタムポリシーで Authorization を転送
  • CloudFront の反映を待たない: 設定変更後 10 分程度かかる。ブラウザをキャッシュしない状態(シークレットタブ or Ctrl+Shift+R)で確認
  • OAC の SourceArn を忘れる: Distribution ID を確定してから S3 Bucket Policy を更新

参考

生きているコード

本ドキュメントで扱ったパターンの完全な動作コードは、メンター側リポジトリの参照ブランチで確認できます。

  • 対応 Week: W20
  • 参照ブランチ:
  • W20: reference/week-20
  • 対応 checklist 項目: M12

ブランチの作り方・見方は b00-curriculum-map を参照してください。

📚 beginner-stepup 全 53 章
導入編 13 章
  1. 1. 📄Web とは何か
  2. 2. 📄URL を打ってから画面が表示されるまで
  3. 3. 📄ネットワーク基礎(TCP/IP・DNS・HTTPS)
  4. 4. 📄【発展】物理層から通信が成立するまで(電力・Ethernet・Wi-Fi・Bluetooth)
  5. 5. 📄WSL2・Docker セットアップ詳細(Windows 向け)
  6. 6. 📄環境構築の段階的導入(macOS / Windows)
  7. 7. 📄カリキュラム全体マップ(Week × 教材 × 参照ブランチ × 要求チェックリスト)
  8. 8. 📄このカリキュラムの使い方(SQL・Python・Dify経験者向け)
  9. 9. 📄シェル・ターミナル基礎
  10. 10. 📄Windows で完全にゼロから始める開発環境構築(Week 1)
  11. 11. 📄Git基礎
  12. 12. 📄GitHubワークフロー
  13. 13. 📄パッケージ管理(pnpm workspace)
応用編 16 章 ● この編を閲覧中
  1. 1. 📄AWSインフラ基礎
  2. 2. 📄AWS Budget Alert の設定(Month 5 Week 17)
  3. 3. 📄環境変数管理
  4. 4. 📄Bastion EC2 と SSH ProxyJump(Month 5 Week 18)
  5. 5. 📄CI/CD基礎
  6. 6. 📄ECR への Docker イメージ push と App EC2 デプロイ(Month 5 Week 19)
  7. 7. 📄テスト設計の基本
  8. 8. 📄CloudFront + S3 + ALB で公開する(Month 5 Week 20)
  9. 9. 📄CLAUDE.md・プロジェクト設定
  10. 10. 📄PR レビュー 5 観点ルーブリック(全 Week 共通)
  11. 11. 📄タスク分解・仕様の書き方
  12. 12. 📄Playwright で E2E テスト(Month 6 Week 22)
  13. 13. 📄生成コードのレビュー・デバッグの勘所
  14. 14. 📄Trivy で脆弱性スキャン(Month 6 Week 23)
  15. 15. 📄CloudWatch Logs の読み方と運用(Month 6 Week 23)
  16. 16. 📄PDF ポートフォリオの自動生成(Month 6 Week 24)