CloudFront経由で 配信ファイルをfetchしたら CORS error…原因と「Response Headers Policy」での解決
背景
「S3上のファイルを読み込み → アプリで編集 → 再度S3へ保存」するアプリを作りました。
- ユーザー(ブラウザ)
- CloudFront 経由で S3 のファイルを GET(fetch)
- 取得したファイルを展開して編集
- 編集結果をS3へアップロード(※アップロード経路はREST API経由)
ところが、CloudFront配信のファイルを fetch() で取得したところ、ブラウザで CORS error が発生しました。
本記事では、
- なぜCORSエラーが起きるのか
- どこでCORSを設定すべきか(S3? CloudFront?)
- CloudFrontの Response Headers Policy での対応
- 「CORS-With-Preflight」を選んだ理由と他の選択肢
をまとめます。
発生した事象
fetch() したリクエストをネットワークタブで確認したところ CORS error と表示されており、しかし詳細を確認すると 200 OK になっているのであれ?となってしまいました、、、
CORSとは(超ざっくり)
CORS(Cross-Origin Resource Sharing)は、別オリジンのリソースをブラウザ上のJavaScriptから読むことを制御するための仕組みです。
重要なのはここで、CORSは「サーバーが通信を拒否する仕組み」というより、ブラウザが“レスポンスをJSに渡すかどうか”を制御する仕組みです。
そのため、サーバーが正常に返していても(=ネットワークとしては成功していても)、
レスポンスに Access-Control-Allow-Origin などがないと、ブラウザがJSに内容を見せてくれず CORS error になります。
原因:CORSは“ブラウザの保護機構”で、レスポンスヘッダーが必要
CORS(Cross-Origin Resource Sharing)は、別オリジンへのリクエストをブラウザが安全に許可する仕組みです。
別オリジンのリソースをJS(fetch等)から読む場合、サーバー側がレスポンスに Access-Control-Allow-Origin などのCORSヘッダーを返さないと、通信自体は成功していても ブラウザがレスポンスをアプリに渡してくれません(=CORS error)。
CORSの基本や、プリフライト(OPTIONS)についてはMDNが分かりやすいです。MDN CORS
どこでCORSを設定するべきか?(S3 vs CloudFront)
S3を直接叩くなら、S3バケットのCORS設定で対応するのが王道です。
ただし今回は「CloudFront経由で配信」しているため、次の選択肢が出てきます。
選択肢A:S3(オリジン)側でCORSヘッダーを返す
- すでにS3側でCORSを管理しているなら一貫性がある
- ただし、配信経路が増えると(CloudFront/ALB/別CDN等)統一が崩れがち
選択肢B:CloudFrontでCORSヘッダーを付与する(今回)
CloudFrontには Response Headers Policy があり、レスポンスにCORS/セキュリティ/カスタムヘッダーを付けられます。:contentReference[oaicite:2]{index=2}
これを使うと「オリジン側をいじらず、CloudFrontで配信時にCORSヘッダーを付ける」が可能です。
今回の対応:Response Headers Policy に「CORS-With-Preflight」を設定
結論として、CloudFrontのビヘイビア(Cache behavior)に対して、
マネージドレスポンスヘッダーポリシー CORS-With-Preflight を関連付けました。
このポリシーは「プリフライト(OPTIONS)を含むCORSリクエストを許可する」ためのAWS管理ポリシーです。
CORS-With-Preflight が付与する代表的ヘッダー(要旨)
AWSドキュメント上、以下のようなCORSヘッダーが設定されています(要約)。
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUTAccess-Control-Expose-Headers: *
(※「オリジンがすでにこれらのヘッダーを返す場合は、CloudFrontは受け取った値を使う」挙動も明記されています)
なぜ「CORS-With-Preflight」を選んだか
「GETしかしてないのにプリフライト?」と思うかもしれませんが、実運用では次の理由で プリフライトが突然発生しがちです。
- GETでも、
Authorization等の“シンプルでないヘッダー”を付けるとプリフライトになる -
PUT/POSTでアップロードをブラウザから直接行う(署名付きURL等)とプリフライトになる - クライアント実装の変更で、ある日突然プリフライトが発生する
そのため、最初から Preflight込みで許可しておくと「将来の変更でまたハマる」を防げます。
また、AWS管理ポリシーなので
- 設計が早い(カスタム設計が不要)
- 運用が軽い(変更管理が少ない)
というメリットもあります。
Response Headers Policy の“他の選択肢”と使い分け
CloudFrontには、CORS関連だけでも複数のマネージドポリシーがあります。
| ポリシー | 何をしてくれる? | どういう時に向く? |
|---|---|---|
| SimpleCORS |
Access-Control-Allow-Origin: * を付与(シンプルCORS向け) |
画像など「単純GETで十分」な配信 |
| CORS-With-Preflight | プリフライト(OPTIONS)含むCORSを許可 | fetch + 将来の拡張(ヘッダー追加/PUT等)に強い |
| CORS-and-SecurityHeadersPolicy | SimpleCORS + セキュリティヘッダー | “配信物にセキュリティヘッダーも載せたい”時 |
| CORS-with-preflight-and-SecurityHeadersPolicy | CORS-With-Preflight + セキュリティヘッダー | CORSもセキュリティもまとめて入れたい時 |
| SecurityHeadersPolicy | セキュリティヘッダーのみ | 既にCORS不要で、セキュリティヘッダーだけ足したい時 |
「CORS-with-preflight-and-SecurityHeadersPolicy」にしなかった理由
セキュリティヘッダー(HSTSやX-Frame-Options等)をCloudFrontで付けるのは便利ですが、
- 既にアプリ側/別経路で付与している
- ヘッダー方針(例:CSP、iframe埋め込み可否)を別途設計したい
などの場合、まずCORSだけ最小限で直す判断もあります。
注意:Access-Control-Allow-Origin: * は万能ではない
Access-Control-Allow-Origin: * は「どのオリジンからの読み取りも許可」なので、公開してよい静的ファイルには便利です。
一方で、Cookie等の資格情報(credentials)を伴うCORSではワイルドカードが使えません。
もし
- 特定のフロントエンドドメインだけ許可したい
-
Access-Control-Allow-Credentials: trueが必要 - 許可メソッド/ヘッダーを絞りたい
なら、マネージドではなく カスタム Response Headers Policy を作るのが安全です(CloudFrontはCORS設定項目としてAccess-Control-Allow-Credentials等も持っています)。