0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【CloudFront】マルチオリジン設定時にリクエストパスがオリジンへ転送される問題を解消する

Last updated at Posted at 2025-01-20

要約

  • Amazon CloudFrontにおいてマルチオリジンを設定した場合、リクエストパスがオリジンへ転送されてしまう問題がある
  • CloudFront Functionsをビューワーリクエストに設定することによって、パスの削除を行う。これにより、問題を解決する
  • しかし、パスの削除によってキャッシュキーが競合する危険あり
  • 同じくCloudFront Functionsによって削除したパスをクエリパラメータに追加する。これにより、問題を解決する

マルチオリジン設定時にリクエストパスがオリジンへ転送される問題

問題について

Amazon CloudFrontでは複数のオリジン(マルチオリジン)を設定することができます。
ビヘイビアにパスパターンを設定することで、「このパスにリクエストが来た時はオリジンA、あのパスにリクエストが来た時はオリジンB」といったように振り分けが可能です。

マルチオリジンを設定した際、リクエストパスがオリジンへのリクエスト時にも転送されるという問題が発生します。

例を示します。

オリジンサーバーとして以下の二つが存在するとします。

  • example1.com
  • example2.com

サーバーのドメイン/ファイル名へアクセスすることでファイルをダウンロードすることができます。
(例: example1.com/sound.mp3)

また、CloudFrontディストリビューションとして、以下が存在するとします。

  • foobar.cloudfront.net

このディストリビューションには以下のようなビヘイビアが設定されています。

  • パスパターン: /example1/*, オリジンサーバー: example1.com
    • foobar.cloudfront.net/example1/*へアクセスがあった場合、example1.comがオリジンとなる
  • パスパターン: /example2/*, オリジンサーバー: example2.com
    • foobar.cloudfront.net/example2/*へアクセスがあった場合、example2.comがオリジンとなる

この時、クライアントからhttps://foobar.cloudfront.net/example1/sound.mp3へのリクエストを行います。
以下のようにリクエストが行われます。

クライアント(ビューワー)
|
| https://foobar.cloudfront.net/example1/sound.mp3
|
CloudFrontディストリビューション(foobar.cloudfront.net)
|
| https://example1.com/example1/sound.mp3
|                      ~~~~~~~~
オリジンサーバー(example1.com)

CloudFrontディストリビューションからオリジンサーバーへのリクエストURLに注目してください。
本来、オリジンサーバーに対してリクエストする際はサーバーのドメイン/ファイル名としたい訳です。
しかし、クライアント→CloudFrontディストリビューションのリクエストパスが余計に転送されてきています。

これによりsound.mp3を取得したいはずが、example1/sound.mp3に対する意図しないリクエストが発生してしまいます。

解決方法

ビューワーリクエストにCloudFront Functionsを設定することで解決します。
CloudFront Functionsにてリクエストパスを削除するという対応を行います。

例えば、以下のようなCloudFront Functionsを作成し、それぞれのビヘイビアのビューワーリクエストに設定します。

// example1.comに対応するビヘイビアに設定するFunction

// ここはKeyValueStoreを使って外側から変更できるようにしても良い
const pathToDelete = '/example1'

function handler(event) {
    const request = event.request;
    request.uri = request.uri.replace(pathToDelete, '/');
    return request;
}
// example2.comに対応するビヘイビアに設定するFunction

// ここはKeyValueStoreを使って外側から変更できるようにしても良い
const pathToDelete = '/example2' // example1.com版とはここが変わっているだけ

function handler(event) {
    const request = event.request;
    request.uri = request.uri.replace(pathToDelete, '/');
    return request;
}

ちなみに、CloudFront Functionsではなく、Lambda@Edgeを利用することも可能です。
その際は、ビューワーリクエストではなくオリジンリクエストに設定します。
オリジンリクエストに設定した場合、後述の「キャッシュ競合の危険性」が発生しなくなるためです。
(今回CloudFront Functionsを利用しているのはコストが圧倒的に安いためです)

キャッシュ競合の危険性

問題の内容

CloudFront Functionsを利用してパスの削除を行うと、キャッシュが競合する危険が生まれます。

foobar.cloudfront.net/example1/sound.mp3へアクセスした際、sound.mp3のキャッシュキーは/sound.mp3となります。
/example1/sound.mp3ではありません。なぜなら、ビューワーリクエストのCloudFront Functionsで/example1部分を削除してしまっているからです。

foobar.cloudfront.net/example2/sound.mp3へアクセスした際、sound.mp3のキャッシュキーは同じく/sound.mp3となります。

したがって、foobar.cloudfront.net/example1/sound.mp3foobar.cloudfront.net/example2/sound.mp3のキャッシュが共通になってしまいます。

キャッシュが共通になってしまっても問題ない状況であれば良いのですが、それを避けたい場合、さらなる対処を講じる必要があります。

解決方法

CloudFront Functionsに処理を追加して、削除したパスをクエリパラメータとしてくっつけます。
クエリパラメータ名はなんでも良いですが、とりあえずここではdeleted_pathにしておきます。

foobar.cloudfront.net/example1/sound.mp3

foobar.cloudfront.net/sound.mp3?deleted_path=/example1
(/example1を削除してクエリパラメータをくっつける)

その上で、追加したクエリパラメータをキャッシュキーとして追加する必要があります。
ビヘイビアに設定してるキャッシュポリシーのキャッシュキー設定のクエリ文字列に、追加したクエリパラメータ名(ここではdeleted_path)を追加します。

CloudFront Functionsのコード例

const pathToDelete = '/example1';

function handler(event) {
    const request = event.request;
    
    // URIから指定されたパスを削除
    request.uri = request.uri.replace(pathToDelete, '/');
    
    // クエリパラメータを追加
    const queryString = request.querystring || '';
    const newQueryString = queryString 
        ? `deleted_path=${pathToDelete}&${queryString}`
        : `deleted_path=${pathToDelete}`;
    
    request.querystring = newQueryString;
    
    return request;
}

ちなみに、リクエスト時に既にクエリパラメータを付加している場合はdeleted_pathを一番最初のクエリパラメータとしてくっつけます。
foobar.cloudfront.net/example1/sound.mp3?param=val

foobar.cloudfront.net/sound.mp3?deleted_path=/example1&param=val

なぜかというと、キャッシュ削除のことを考えるとその方が都合が良いからです。
CloudFrontにおけるキャッシュ削除時には*(ワイルドカード)を利用することができますが、一番最後にしか指定することができません。
deleted_pathが最初に来ていることによって、以下のようにキャッシュを削除することができます。
/sound.mp3?deleted_path=/example1&*
(/example1配下のキャッシュを全て削除する)

最後に

CloudFrontは機能としてはシンプルに保ち、それ以上のことをしたければCloudFront FunctionsやLambda@Edgeで補う、という方針だとは思いますが、流石にこれは、、、という気持ちになりました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?