こんにちは。ごえです。
AWS構築を担当し、日々新しい知識と出会っています。
今回は、「CloudFront + S3」で静的コンテンツを配信する構成の設定時につまずいたことの記録です。
よくある構成ですが、はじめのうちは理解しづらいと思うので
よかったら参考にしてください。
まずは用語の説明から
■ ビヘイビア(Behavior)
アクセスに対してCloudFrontがどのように動作するかを設定する項目です。
(1)どのようにCloudFrontがキャッシュするのか
(2)どのHTTP メソッドを許可するのか(GET, HEAD, OPTIONSなど)
(3)どのオリジンにリクエストを転送するか
このように様々な設定項目があります。
今回の記事で着目する設定項目は、(3)の「どのオリジンにリクエストを転送するか 」です。
Behaviorでは、アクセスされたURLのパスに応じて、どのオリジン(S3やEC2など)にリクエストを送るかを決定します。
例えば、
- /img/* → S3にリクエスト
- /* → EC2にリクエスト
のように、URLのパスをもとに転送先を切り替える役割を持ちます。
■ S3オリジン
CloudFrontがコンテンツを取得しに行く「データの保存先(配信元)」の一つです。
S3オリジンを設定すると、CloudFrontはS3バケットに保存された画像やファイルを取得し、ユーザーに配信します。
【結論】
CloudFront + S3 構成でコンテンツが見れない場合の対処
この記事は、筆者の奮闘記録も兼ねているので長くなります。
そのため、「S3に置いてる画像が見れない!」という方は以下の設定を参考にしてみてください。
(設定は一例です。BehaviorやS3オリジンのオリジンパス(Origin Path)の設定に不備がある場合は
改善するかもしれません)
【目標】https://example.com/img/image.png のURLでS3上の画像を表示する
・Behavior のパスパターン
⇒ /img/*
・S3オリジンのオリジンパス
⇒設定しない
画像は以下のよう「img」配下に配置
s3://example-bucket/img/image.png
パスパターンとオリジンパス の設定が適切であれば、CloudFrontの設定でミスしやすい箇所はクリアできると思います。
以降は、詳細を書いていきます。
CloudFrontでS3オリジンのルートとBehaviorのパス設計を誤ると何が起きるのか
CloudFrontでS3とEC2を組み合わせる構成はよく使われますが、
その中でも見落とされがちなのが「パスの設計」です。
・Behaviorのパスパターン
・S3オリジンのオリジンパス
この2つは独立した設定に見えますが、実際には密接に関係しています。
そしてここを誤ると、
・なぜかS3のファイルが502エラーになる
・意図しないオリジンにルーティングされる
・設定は正しそうなのに動かない
といった現象が発生します。
この記事では、CloudFrontの設定方法を整理します。
https://example.com/ → EC2(アプリ)
https://example.com/img/ → S3(画像)
S3には以下のように画像データが配置されています。
s3://example-bucket/img/image.png
CloudFrontは何を見ているのか
CloudFrontはリクエストを以下の順序で処理します。
リクエストのパスを見る(例: /img/image.png)
↓
Behaviorのパスパターンと照合する
↓
マッチしたBehaviorに紐づくOriginを選択する
↓
必要に応じてオリジンパス を付与する
↓
オリジンにリクエストを送る
Behaviorとオリジンパスの関係は「連結」である
ここが一番ハマるポイントです。(私が最初につまずいたポイントです)
CloudFrontは、最終的にオリジンへ送るパスをこう作ります。
オリジンリクエストパス = オリジンパス + リクエストパス
つまり「足し算」です。
正しい設計パターン
今回の構成では、以下が最もシンプルで安全です。
パスパターン:/img/*
オリジン :S3
Origin Path :なし
②
パスパターン:/*
オリジン :EC2
Origin Path :なし
※S3には以下のように画像データが配置されているとします。
s3://example-bucket/img/image.png
このときの流れはこうなります。
リクエスト: /img/image.png
↓
①のパスパターンにマッチ(/img/*)
↓
Origin Path:なし
↓
S3に /img/image.png をリクエスト
👉 S3の構造と一致するため、正常に取得できます
なぜOrigin Pathを設定すると壊れるのか
例えば、以下のように設定した場合:
Behavior : /img/*
Origin Path: /img
CloudFrontはこう解釈します。
/img/image.png
↓
/img + /img/image.png
↓
/img/img/image.png
👉 S3には存在しないパスになり502エラーが返ります。
どこでパスを表現するべきか
この問題の本質は「どこでパスを表現するか」です。
選択肢は2つあります
パターンA
S3バケット配下に /img/ のパスを持たせる
Behavior も /img/*
Origin Pathは使わない
パターンB
S3はルート直下にコンテンツを置く
Behavior で /img/*
Origin Pathで /img を補う
👉 CloudFront側で無理やり調整する設計
上記の内、パターンAがいいです。
なぜパターンAが良いのか
理由はシンプルで、
👉 パターンBでは、以下のルート直下の競合が発生する場合があるためです。
例えば以下のような設計をすると破綻します。
S3 → バケットのルート直下にコンテンツを配置
EC2 →ドキュメント ルート直下にコンテンツを配置
Behavior → /* のみ
この場合、
S3:/image.png
EC2:/index.php
どちらも同じ「/配下」なので、
👉 CloudFrontはどちらに送るか判断できません
なぜか?
CloudFrontは「ファイルの種類」や「存在」では判断しません。
👉 あくまでパスパターンのみで決定します
つまり、今回の場合はS3のルート直下には配信するコンテンツを置かない方がいいと言えます。
実務では以下を守ると事故りません。
① オリジンごとにパスを分ける
/img/* → S3
/* → EC2
② Origin Pathはなるべく使わない
使うと「見えない変換」が増える
③ S3の構造をURLに寄せる
s3://bucket/img/xxx.png
⇔
https://example.com/img/xxx.png
👉 これが一番分かりやすい
おわりに
最初は、なぜ動かないのか分かりませんでしたが、一度理解してしまえばすぐに解決しました。
ぜひ参考にしてください。
最後までお読みいただきありがとうございました。

