CloudFrontでS3が502になる原因はこれ:BehaviorとOrigin Pathの関係を解説

目次

こんにちは。ごえです。
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

👉 これが一番分かりやすい

おわりに

最初は、なぜ動かないのか分かりませんでしたが、一度理解してしまえばすぐに解決しました。
ぜひ参考にしてください。

最後までお読みいただきありがとうございました。