AWS中級 #7 CloudFront で静的サイトを配信

読了 9分

#3 S3 の静的ホスティングで 1 つの限界を見ました — HTTPS が直接できず、ユーザーに近いキャッシュもなく、カスタムドメイン + SSL も無理。その 3 つを一気に解く場が CloudFront です。

CloudFront は AWS の CDN (Content Delivery Network)。世界中 600+ 箇所の Edge Location にキャッシュを置き、ユーザーから最も近い Edge が応答します。静的サイトだけでなく動的 API も通せる万能なサービスです。

この記事では CloudFront の形 → S3 + CloudFront パターン → OAC → キャッシュポリシー → 無効化を 1 本の線で通していきます。

CDN が解く問題 #

S3 静的サイトをそのまま使うと次が悪くなります。

問題CloudFront が解く場
米国ユーザーがソウル S3 から取得すると 200ms+Edge キャッシュで ms 応答
HTTPS が直接できないACM 証明書 + 自動 HTTPS
Egress 費用が高い ($0.09/GB)CloudFront → インターネット ($0.085/GB) + キャッシュヒットは無料
キャッシュヘッダー / 圧縮 / HTTP/2 / HTTP/3 を自前自動
WAF / Shield を自前CloudFront と統合
DDoSAWS Shield Standard 自動

S3 + CloudFront はすべての静的サイトの標準。

CloudFront の形 #

CloudFront Distribution の場
 ユーザー
  │ HTTPS request: example.com/path/to/asset.js
┌────────────────────────────────────┐
│       CloudFront Edge Location      │
│       (ユーザーに最も近い場所)            │
│                                      │
│    キャッシュ確認                       │
│      ├── HIT → キャッシュから応答          │
│      └── MISS                        │
└────────────────────────────────────┘
                │ MISS 時
        ┌────────────────────┐
        │   Cache Behavior   │
        │  (path マッチ / ポリシー)│
        └────────┬───────────┘
            ┌────────┐
            │ Origin │  ← S3、ALB、EC2、Lambda Function URL ...
            └────────┘

中心の場 3 つ:

  • Distribution — ひとまとまりの設定 (ドメイン + 証明書 + 動作)
  • Origin — キャッシュミス時に取りに行く場 (S3、ALB、自前ドメイン等)
  • Cache Behavior — path マッチ + キャッシュポリシー + Origin マッチ

Distribution 作成 #

シンプルな CloudFront Distribution
aws cloudfront create-distribution \
  --origin-domain-name my-bucket.s3.ap-northeast-2.amazonaws.com \
  --default-root-object index.html

設定の場:

オプション内容
Origin domainS3 バケットまたは ALB DNS または自前ドメイン
Default root object/ リクエスト時に返すファイル (index.html)
Alternate domain (CNAME)example.comwww.example.com
SSL certificateACM (必ず us-east-1)
Price classどの大陸の Edge まで使うか
LoggingS3 にアクセスログを保存
WAFWeb ACL 連結

Price Class #

Price Class内容
All世界中のすべての Edge — 費用最大
200米国 / 欧州 / アジア / オーストラリア
100米国 / 欧州 / イスラエル / 一部アジア — 最安

韓国 / 日本ユーザーのみなら 200 以上 推奨。グローバルサービスは All

Origin — どこから取るか #

S3 Origin #

最もよくある場。静的サイト / 画像 / 動画。

S3 Origin
Origin: my-bucket.s3.ap-northeast-2.amazonaws.com
        (REST API endpoint であり、s3-website-... URL ではない)

s3-website-* URL ではなく REST API URL を使うこと。そうすれば OAC (下記) で安全に保護可能。

Custom Origin (ALB / 任意 HTTP サーバー) #

ALB / EC2 / 外部サーバーも Origin になれます。このパターンは 動的 API 加速 の場。

ALB Origin
Origin: my-alb-1234567890.elb.ap-northeast-2.amazonaws.com
HTTPS Port: 443
SSL Protocols: TLSv1.2、TLSv1.3
Origin Path: (なし、または /api)

ALB → CloudFront → ユーザーパターンは WAF + Edge SSL termination + キャッシュ の場。

Lambda Function URL Origin #

Lambda が直接 Origin の場。サーバーレス静的 + 動的サイト。

Origin Group — failover #

Primary Origin が死んだら Secondary へ。マルチリージョン DR の場。

Cache Behavior — ルーティング + キャッシュポリシー #

同じ Distribution 内で path ごとに異なる動作 を割り当てられます。

Cache Behavior の場
Path Pattern   | Origin | Cache Policy | TTL
---------------+--------+--------------+--------
/api/*         | ALB    | NoCaching    | 0
/static/*      | S3     | Optimized    | 1 day
default (*)    | S3     | Optimized    | 1 hour

処理順: path pattern マッチ (具体的な方が先) → マッチなしなら default。

Cache Policy #

キャッシュ動作を定義する場。AWS 提供の既定ポリシー + カスタム。

既定 Cache Policy
CachingOptimized               ← 静的アセット (画像 / JS / CSS)
CachingOptimizedForUncompressedObjects  ← 非圧縮アセット
CachingDisabled                ← API キャッシュなし
Elemental-MediaPackage         ← メディアストリーミング

キャッシュキーの場 (= 何が違えば別キャッシュ項目):

  • Query string — すべて / 一部 / なし
  • Header — どのヘッダーで分けるか
  • Cookie — どのクッキーで分けるか
シンプルな静的アセットのキャッシュキー
Query: 無視
Header: 無視 (または Accept、Accept-Encoding)
Cookie: 無視
言語 / デバイス分離 SSR のキャッシュキー
Query: すべて
Header: Accept-Language、CloudFront-Is-Mobile-Viewer
Cookie: session-id

Origin Request Policy #

Origin に 何を送るか を定義するものです。キャッシュキーとは別の概念です。

Cache PolicyOrigin Request Policy
何を決定キャッシュキー + TTLOrigin に送るヘッダー / クエリ / クッキー
効果キャッシュヒット率動的応答に何が影響するか

Response Headers Policy #

応答ヘッダーを自動追加 / 修正します。Strict-Transport-SecurityX-Content-Type-Options のようなセキュリティヘッダーを付与するためのものです。

推奨セキュリティヘッダー
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; ...

TTL — どれだけキャッシュするか #

キャッシュ TTL の場:

TTL用途
0キャッシュなし。すべてが Origin へ
60 ~ 300 秒よく変わるコンテンツ (ニュース見出し)
1 時間 ~ 1 日一般静的アセット
1 年 (max-age=31536000)hashed filename (app.abc123.js)

Cache-Control ヘッダーの場 #

Origin 応答の Cache-Control ヘッダーが優先 (Min/Default/Max TTL の範囲内なら)。

Origin の Cache-Control 応答
Cache-Control: public, max-age=31536000, immutable   ← hashed asset (1 年)
Cache-Control: public, max-age=300                   ← HTML ページ (5 分)
Cache-Control: no-store                              ← 絶対キャッシュしない

運用での使い分け:

  • HTML = 短く (60s ~ 5min) — 新ビルドを早く見せる
  • JS / CSS / hash 付き画像 = 1 年 + immutable — 内容が変われば新ファイル名
  • API = 通常キャッシュなし (または short)

S3 + CloudFront パターン #

静的サイトの標準セットアップ。

S3 + CloudFront 標準パターン
ユーザー
example.com (Route 53 Alias) ──▶ CloudFront
                                    ▼ (OAC)
                                  S3 my-bucket  ← Public Access Block すべて有効
                                    ▼ Bucket Policy
                                  CloudFront のみ GetObject 許可

OAC — Origin Access Control #

旧式は OAI (Origin Access Identity)。新推奨は OAC

OAC の場
- S3 の PAB をすべて有効 (絶対 public にならない)
- Bucket Policy が CloudFront の OAC のみ許可:

  {
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": { "Service": "cloudfront.amazonaws.com" },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EXXXXXX"
        }
      }
    }]
  }

これで:

  • S3 直接アクセスはすべて遮断s3.amazonaws.com/my-bucket/x で来ると 403
  • CloudFront だけ通過 — Edge キャッシュ経由でのみ応答
  • Egress 費用削減 + キャッシュヒット高速化 + WAF 適用

OAI vs OAC #

OAI (旧)OAC (新)
認証方式CloudFront の仮想 IAM IdentitySigV4 署名
新リージョン / 機能部分対応すべて対応
KMS-encrypted S3追加設定が必要なめらか
推奨移行を検討新規のデフォルト

新規セットアップは OAC、旧は OAI のまま使いつつ移行。

無効化 (Invalidation) #

キャッシュされたオブジェクトを 満了前に強制クリア する場。デプロイ直後に新バージョンを即時表示。

Invalidation 作成
aws cloudfront create-invalidation \
  --distribution-id EXXXXXX \
  --paths "/*"

用途:

  • 静的サイトデプロイ後 /* 1 行
  • 特定ページのみ更新: /index.html /about.html
  • ワイルドカード: /blog/2026/*

費用の罠 #

CloudFront の invalidation は 最初の 1000 path / 月は無料、その後 path あたり $0.005。/* 1 回 = 1 path なので頻繁に呼んでも OK。

Cache Versioning パターン — 無効化を使わない #

本当の運用では 無効化を使いません。代わりに:

Hashed filename パターン
ビルド成果:
  /static/app.abc123.js   ← コンテンツハッシュ
  /static/app.def456.js   ← 次のビルドは別ハッシュ

HTML は短くキャッシュ (1 分)、JS/CSS は 1 年:
  Cache-Control: public, max-age=31536000, immutable

新規デプロイ時:
  /index.html が新ハッシュの JS を指す
  HTML キャッシュ満了 (1 分) → 新 JS が自動ロード

このパターンなら invalidation なしで即時反映 + キャッシュ効率最大。

Lambda@Edge / CloudFront Functions #

Edge で リクエスト / 応答を横取りしてコード実行 できる機能です。

CloudFront Functions #

  • JavaScript ES5 のみ
  • リクエスト / 応答の viewer 段階のみ
  • 5ms 以内、1MB 以内メモリ
  • 非常に安い ($0.10 / 100 万)
  • 用途: redirect、ヘッダー追加、A/B テスト、認証トークンチェック
シンプルな redirect
function handler(event) {
  var request = event.request;
  if (request.uri === '/old-page') {
    return {
      statusCode: 301,
      statusDescription: 'Moved Permanently',
      headers: { 'location': { value: '/new-page' } }
    };
  }
  return request;
}

Lambda@Edge #

  • Node.js / Python
  • 4 段階 (viewer request / origin request / origin response / viewer response)
  • 5 秒 (viewer) / 30 秒 (origin)
  • 高くて遅いが強力
  • 用途: 動的コンテンツ変換、マルチリージョンルーティング、複雑な認証

最近はできるだけ CloudFront Functions から始め、足りないときだけ Lambda@Edge。

Signed URL / Signed Cookie — 非公開コンテンツ #

有料動画 / 専用ダウンロード のような場。CloudFront の署名で時間 / IP 制限。

Signed URL の形
https://d111111abcdef8.cloudfront.net/video.mp4?
  Expires=1714992000&
  Signature=...
  Key-Pair-Id=APKAEX...
  • Signed URL — 1 つのリソースに対する 1 つの URL
  • Signed Cookie — 1 ユーザーが複数リソースアクセス (サイト単位の定期購読)

S3 の presigned URL と異なる場 — CloudFront Edge で検証、よりグローバル + キャッシュ可能。

CloudFront と ACM の場 #

#6 で扱った ACM のリージョンルール:

CloudFront 証明書は常に us-east-1
ACM (us-east-1)
  ├── *.example.com         ← CloudFront が使用
  └── example.com

ACM (ap-northeast-2)
  ├── api.example.com       ← ALB が使用

コンソールで証明書取得時、必ず N. Virginia (us-east-1) にリージョン切替。

圧縮と HTTP/2 / HTTP/3 #

CloudFront が自動で:

  • gzip / Brotli 圧縮 — オプション有効化
  • HTTP/2 — デフォルト
  • HTTP/3 (QUIC) — オプション
  • TLS 1.3 — Security Policy で

この場は ALB 直接公開より CloudFront の方がなめらか。

よくハマる罠 #

1) S3 がいまだに Public #

OAC をセットアップしたのに PAB を有効化せず、S3 が直接アクセス可能 — PAB 4 つすべて有効化 + Bucket Policy が CloudFront のみ許可。

2) CNAME mismatch #

ACM 証明書が *.example.com なのに Distribution の Alternate domain が app.example.com (ワイルドカード子なので OK) — 誤って書くと SSL 証明書 mismatch エラー。証明書 SAN とドメインを正確に揃える。

3) キャッシュが強すぎて新デプロイが見えない #

max-age=31536000 の HTML をそのままキャッシュ → 新デプロイ後もユーザーが旧 HTML を取得。HTML は短く / hashed asset だけ長く

4) クエリのキャッシュキーが誤っている #

クエリ全部をキャッシュキーに → 同じファイルでも ?v=1?v=2 ごとに別キャッシュ → ヒット率 0%。本当に影響するクエリだけキャッシュキーに。

5) Default root object 抜け #

/ でリクエストすると 403/404。Default root object = index.html を設定。

6) Origin に送る Host ヘッダー #

S3 Origin の場合 CloudFront が自動で Host ヘッダーを処理。だが ALB / 任意サーバーは Origin Request Policy で Host ヘッダーを伝達 する場を明示。

7) Origin の SSL 証明書が信頼されない #

Custom Origin (自前サーバー) が self-signed → CloudFront が拒否。ACM 発行証明書または信頼された CA を。

8) index.html redirect #

S3 静的サイトの /about//about/index.html 自動変換が OAC の場では効きません。CloudFront Function で path rewrite を直接:

path rewrite Function
function handler(event) {
  var request = event.request;
  var uri = request.uri;
  if (uri.endsWith('/')) {
    request.uri = uri + 'index.html';
  } else if (!uri.includes('.')) {
    request.uri = uri + '/index.html';
  }
  return request;
}

まとめ #

今回押さえたこと:

  • CloudFront = グローバル CDN。Edge 600+ 箇所でキャッシュ応答
  • Distribution + Origin + Cache Behavior の 3 つ
  • Origin = S3 / ALB / Custom HTTP / Lambda URL / Origin Group
  • S3 + CloudFront + OAC が静的サイト標準パターン
  • OAC が新推奨。PAB 4 つすべて有効化、Bucket Policy で CloudFront のみ許可
  • Cache Policy でキャッシュキー (クエリ/ヘッダー/クッキー) + TTL。Origin Request Policy は Origin へ送る場
  • TTL 戦略 — HTML 短く、hashed asset 1 年 + immutable
  • 無効化 はたまに。Hashed filename パターン が無効化を使わない道
  • CloudFront Functions = 速くて安い。Lambda@Edge = 強力だが重い
  • Signed URL / Cookie で非公開コンテンツ
  • ACM 証明書は必ず us-east-1
  • 圧縮 / HTTP/2 / HTTP/3 が自動
  • 罠 — S3 public、CNAME mismatch、キャッシュ強すぎ、クエリキー誤り、default root、Host ヘッダー、Origin SSL、path rewrite

次回 — AWS 上級スタート #

これで AWS 中級 7 編 が締めくくられました。EC2 / VPC / S3 / RDS / Route 53 / ALB / CloudFront という基本道具箱が一所にそろいました。

ここから上に コンテナ / サーバーレス / イベント駆動 の場が積み上がります。AWS 上級 #1 ECS と Fargate では、EC2 上に直接アプリを乗せていた場をコンテナへ移し、ASG のコンテナ版である ECS / Fargate の運用を整理します。

X