
CloudFront のレスポンスヘッダーポリシーで CORS を設定しても、ブラウザで CORS エラーになる原因を教えてください
困っていること
S3 のバケットをオリジンにした Amazon CloudFront のディストリビューションから、別のサイトのページに JSON を配信しています。
ディストリビューションのビヘイビアには、CORS の Access-Control-Allow-Origin にそのサイトのオリジンを書いたレスポンスヘッダーポリシーを付けました。ところが、ブラウザのコンソールに次のエラーが出て、fetch が失敗します。
Access to fetch at 'https://<assets-domain>/case3/data.json' from origin 'https://<page-domain>' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
別のサイトから読み込むと、Access-Control-Allow-Origin に自分のオリジンと違う値が返ることもあります。レスポンスヘッダーポリシーを付けているのに、CORS のヘッダーが正しく返らない原因を知りたいです。
どう対応すればいいの?
原因は 2 つ考えられます。
1 つ目は、ポリシーの Access-Control-Allow-Origin に書いた値が、ブラウザが送る Origin ヘッダーの値と一致していないことです。一致しないと、CloudFront は Access-Control-Allow-Origin を付けません。「オリジンのオーバーライド」にチェックを付けていると、オリジンの S3 が返した CORS ヘッダーも使われないため、ヘッダーが無い応答になります。
2 つ目は、オリジンの S3 にも CORS 設定があり、「オリジンのオーバーライド」にチェックを付けないまま、Origin ヘッダーをキャッシュキーに含めていないことです。この場合、先にキャッシュされた、別のオリジン向けの Access-Control-Allow-Origin がそのまま返ります。
どちらが原因かは、応答の Access-Control-Allow-Origin で見分けられます。ヘッダーが無い場合は 1 つ目、自分のオリジンと違う値が返る場合は 2 つ目の可能性が高いです。
原因に応じて、次の対策をご検討ください。1 は 1 つ目の原因への対策です。2 と 3 は 2 つ目の原因への対策で、どちらか一方を行います。4 は、どちらの場合も行ってください。
- CloudFront のコンソールの「ポリシー」→「レスポンスヘッダー」でポリシーを開き、
Access-Control-Allow-Originの値を、ブラウザが送るOriginの値と同じにします。https://www.example.comのように、スキームからホスト名まで(ポートがあればポートまで)書きます。 - オリジンの S3 にも CORS 設定がある場合は、ポリシーの「オリジンのオーバーライド」にチェックを付けて、ポリシーの値を使います。

- オーバーライドせずに S3 の CORS 設定のほうを使う場合は、ビヘイビアのキャッシュポリシーで、ヘッダー
Originをキャッシュキーに含めます。マネージドのキャッシュポリシー CachingOptimized はOriginをキャッシュキーに含めないので、Originを含めるカスタムのキャッシュポリシーに替えます。オリジンリクエストポリシーの CORS-S3Origin は、Originをオリジンに転送するだけで、キャッシュキーには含めません。 - 設定を変えたら、ファイルの無効化(Invalidation)で古いキャッシュを消します。その後、前の応答が残らないシークレットウィンドウか curl で確かめます。
ポリシーの値は、Origin ヘッダーと同じ形で書く
ポリシーの Access-Control-Allow-Origin にスキームを含めて書いた値は、スキームが違うオリジンには一致しません。ポートを書いていない値は、ポートを付けたオリジンにも一致しません。「レスポンスヘッダーポリシーを理解する」のページの例の表でも、http://example.org と書いた値は、https://example.org にも http://example.org:123 にも一致しないとされています。
オリジンのオーバーライドで、どちらの CORS ヘッダーを使うかが決まる
「オリジンのオーバーライド」は、オリジンの応答にも CORS ヘッダーがあるときに、ポリシーとオリジンのどちらのヘッダーを使うかを決める設定です。
trueに設定されていて、オリジンレスポンスで、ポリシーにも CORS ヘッダーが含まれている場合、CloudFront は、レスポンスに対するポリシーの CORS ヘッダーを追加します。次に CloudFront はそのレスポンスをビューワーに送信します。CloudFront はオリジンから受け取ったヘッダーを無視します。falseに設定されており、オリジンレスポンスに CORS ヘッダーが含まれている場合 (CORS ヘッダーがポリシーのものかどうかに関係なく)、CloudFront には、オリジンから受け取りレスポンスに送信する CORS ヘッダーが含まれます。CloudFront は、ビューワーに送信されるレスポンスにポリシー内の CORS ヘッダーを追加しません。
ポリシーのヘッダーは、キャッシュから返す応答にも付きます。そのため、オーバーライドしてポリシーの値を使う場合は、キャッシュされた S3 のヘッダーが返る心配はありません。
CloudFront は、キャッシュから提供するレスポンスとオリジンから転送するレスポンスのヘッダーを変更します。
オーバーライドしないときは、キャッシュされた S3 のヘッダーが返る
S3 の CORS 設定を CloudFront のようなプロキシ越しに使うときは、Origin ヘッダーをキャッシュキーに含める必要があります。
- オリジンヘッダーをキャッシュキーに含めるようにプロキシ設定を構成します。キャッシュキーにオリジンヘッダーを含まないキャッシュプロキシは、異なるオリジンに対して適切な CORS ヘッダーを含まないキャッシュレスポンスを提供する可能性があるため、これは重要です。
確認してみた
東京リージョンの S3 バケット 2 つと、従量制のディストリビューション 2 つを使いました。ページ側のディストリビューション(以下、ページ)には、アセット側のディストリビューション(以下、アセット)から fetch で JSON を読む HTML を置きました。アセットには、同じ JSON を 3 つのパスに置き、ビヘイビアを 3 つ作りました。
| ビヘイビア | キャッシュポリシー | オリジンリクエストポリシー | レスポンスヘッダーポリシー |
|---|---|---|---|
| /case1/* | CachingOptimized | なし | マネージドの CORS-With-Preflight(オリジンの応答に CORS ヘッダーがあれば、そちらを使う) |
| /case2/* | CachingOptimized | CORS-S3Origin | カスタム(オリジンのオーバーライド:false) |
| /case3/* | カスタム(Origin をキャッシュキーに含む) | CORS-S3Origin | カスタム(オリジンのオーバーライド:true) |
カスタムのレスポンスヘッダーポリシーと S3 の CORS 設定には、許可するオリジンとして、ページのオリジンと比較用の https://other.example の 2 つを書きました。

キャッシュに無い状態から、curl で 3 つのパスに、Origin ヘッダーを「ページ」→「https://other.example」→「なし」の順に送りました。応答の Access-Control-Allow-Origin(画像では ACAO)と x-cache を記録しています。

画像の case3 の下にある「Origin」だけの行は、応答の 2 つ目の vary ヘッダーの値が改行されたものです。case3 の 2・3 行目の末尾の「| Vary=Origin」は、画面の幅で折り返されています。
case1 と case2 では、1 回目(ページのオリジン)の応答がキャッシュされました。2 回目の https://other.example からのリクエストにも、Access-Control-Allow-Origin: https://<page-domain> が返っています。リクエストが S3 まで届かず、キャッシュから返ったためです。S3 の CORS 設定は https://other.example も許可していますが、使われませんでした。
case1 のポリシー CORS-With-Preflight の Access-Control-Allow-Origin の値は * です。1 回目に返ったページのオリジンの値は、S3 が付けたものです。このポリシーは、オリジンの応答に CORS ヘッダーがあれば、そちらを使います。
Origin をキャッシュキーに含めた case3 では、3 回とも Miss になり、それぞれのオリジンに合った値が返りました。
次に、case3 のレスポンスヘッダーポリシーの Access-Control-Allow-Origin を、ページのドメインを http:// で書いた値だけに変えました。ローカルの Playwright(Chromium)でページを開くと、case3 だけが次のエラーになりました。

このとき、ページのオリジンを Origin ヘッダーに付けて curl でアクセスすると、応答に Access-Control-Allow-Origin は無く、vary: Origin だけが付いていました。S3 の CORS 設定は、ページのオリジンを許可したままです。オーバーライドしているので、S3 のヘッダーは使われませんでした。ポリシーの値を https:// で書いていたときは、同じページから 3 つとも読めていました。








