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 name | kinoshita-sns-clone-web(グローバル一意) |
| Region | Asia Pacific (Tokyo) |
| Block Public Access | すべてブロック を ON(CloudFront OAC で配信) |
| Bucket Versioning | Disable(カリキュラム用) |
重要: 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 type | Instances |
| Target group name | app-tg |
| Protocol | HTTP |
| Port | 3001 |
| VPC | カリキュラム用 VPC |
| Health check path | /health |
| Health check port | traffic port(3001) |
Register targets で App EC2 を選び、Port 3001 で追加。
2-2. ALB 本体
EC2 → Load Balancers → Create Application Load Balancer:
| 項目 | 値 |
|---|---|
| Load balancer name | sns-clone-alb |
| Scheme | Internet-facing |
| VPC | カリキュラム用 VPC |
| Mappings | Public Subnet × 2(AZ 冗長) |
| Security group | alb-sg(Inbound: 80/443 from 0.0.0.0/0) |
| Listener | HTTP: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 domain | kinoshita-sns-clone-web.s3.ap-northeast-1.amazonaws.com |
| Origin access | Origin Access Control を新規作成(kinoshita-sns-clone-web-oac) |
| Origin type | S3 |
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 domain | ALB の DNS 名 |
| Protocol | HTTP only(CloudFront ↔ ALB を HTTP に) |
| HTTP port | 80 |
3-3. Default Cache Behavior(/* = S3 向け)
| 項目 | 値 |
|---|---|
| Path pattern | Default (*) |
| Origin | S3 オリジン |
| Viewer protocol policy | Redirect HTTP to HTTPS |
| Allowed HTTP methods | GET, HEAD |
| Cache policy | CachingOptimized |
3-4. 追加 Behavior(/api/* = ALB 向け)
CloudFront → Behaviors → Create behavior:
| 項目 | 値 |
|---|---|
| Path pattern | /api/* |
| Origin | ALB オリジン |
| Viewer protocol policy | Redirect HTTP to HTTPS |
| Allowed HTTP methods | GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE |
| Cache policy | CachingDisabled(API レスポンスはキャッシュしない) |
| Origin request policy | AllViewer(認証ヘッダを転送) |
3-5. SPA のルーティング対応
React Router を使っているので、/posts/abc123 に直接アクセスされたら S3 は 404 を返します。CloudFront で 404 を index.html にフォールバック:
Error pages → Create custom error response:
| 項目 | 値 |
|---|---|
| HTTP error code | 404 |
| Customize error response | Yes |
| Response page path | /index.html |
| HTTP response code | 200 |
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 にする:
- Route 53 → Hosted zones → 自分のドメイン
- ACM(us-east-1 リージョン必須)で証明書を発行:
*.example.com+example.com - ACM の DNS 検証レコードを Route 53 に追加
- CloudFront の Alternate domain names に
app.example.comを追加、証明書を ACM のものに - 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 を更新
参考
- CloudFront 公式: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/
- S3 OAC: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
- ACM(us-east-1 必須): https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-regions.html
- 前提: b18-bastion / b19-ecr-push
- 続き: b22-playwright(公開後の E2E)/ b23-trivy(セキュリティ診断)
生きているコード
本ドキュメントで扱ったパターンの完全な動作コードは、メンター側リポジトリの参照ブランチで確認できます。
- 対応 Week: W20
- 参照ブランチ:
- W20:
reference/week-20 - 対応 checklist 項目: M12
ブランチの作り方・見方は b00-curriculum-map を参照してください。
- 1. 📄Web とは何か
- 2. 📄URL を打ってから画面が表示されるまで
- 3. 📄ネットワーク基礎(TCP/IP・DNS・HTTPS)
- 4. 📄【発展】物理層から通信が成立するまで(電力・Ethernet・Wi-Fi・Bluetooth)
- 5. 📄WSL2・Docker セットアップ詳細(Windows 向け)
- 6. 📄環境構築の段階的導入(macOS / Windows)
- 7. 📄カリキュラム全体マップ(Week × 教材 × 参照ブランチ × 要求チェックリスト)
- 8. 📄このカリキュラムの使い方(SQL・Python・Dify経験者向け)
- 9. 📄シェル・ターミナル基礎
- 10. 📄Windows で完全にゼロから始める開発環境構築(Week 1)
- 11. 📄Git基礎
- 12. 📄GitHubワークフロー
- 13. 📄パッケージ管理(pnpm workspace)
- 1. 📄AWSインフラ基礎
- 2. 📄AWS Budget Alert の設定(Month 5 Week 17)
- 3. 📄環境変数管理
- 4. 📄Bastion EC2 と SSH ProxyJump(Month 5 Week 18)
- 5. 📄CI/CD基礎
- 6. 📄ECR への Docker イメージ push と App EC2 デプロイ(Month 5 Week 19)
- 7. 📄テスト設計の基本
- 8. 📄CloudFront + S3 + ALB で公開する(Month 5 Week 20)
- 9. 📄CLAUDE.md・プロジェクト設定
- 10. 📄PR レビュー 5 観点ルーブリック(全 Week 共通)
- 11. 📄タスク分解・仕様の書き方
- 12. 📄Playwright で E2E テスト(Month 6 Week 22)
- 13. 📄生成コードのレビュー・デバッグの勘所
- 14. 📄Trivy で脆弱性スキャン(Month 6 Week 23)
- 15. 📄CloudWatch Logs の読み方と運用(Month 6 Week 23)
- 16. 📄PDF ポートフォリオの自動生成(Month 6 Week 24)