はじめに
• CloudFront経由でS3のCORS(Cross Origin
  Resource Sharing)を利用しようと思って少し
  ハマったので、CORSの仕様自体
  (http://www.w3.org/TR/cors/)から一度ちゃん
  と読んでまとめてみた。
通常のCORSリクエスト
リクエストにはOriginヘッダが必要!サーバー側の実装に依
存するが、仕様上ではOriginヘッダがあるときのみ、CORSリ
クエストとして認識し、レスポンスのヘッダ内でAccess-
Control-*を返す。
             ヘッダ
           Origin: http://test-domain

                        GETリクエスト
                                                             WEB
 Browser                                                     サー
                                                             バー
                       レスポンス
             ヘッダ
           Access-Control-Allow-Origin: http://test-domain
           Access-Control-Allow-Methods: GET
           Access-Control-Max-Age: 3000
           Access-Control-Allow-Credentials: true
CORSレスポンス
CORSのレスポンスには、ヘッダ内に以下のような項目が含
まれている。これらが含まれていないとCORSにはならな
い。リクエストにOriginヘッダがなかったり、後述のpreflight
リクエストに失敗したりするとこれらのヘッダはつかない。
  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Max-Age
  • Access-Control-Allow-Credentials

最低限、Access-Control-Allow-Originが含まれていればブラウ
ザ側で解釈してくれる。(少なくともChromedでは)

ex. Access-Control-Allow-Origin: http://test-domain
カスタムヘッダ付きCORSリクエスト
リクエスト内にX-hogeのようなカスタムヘッダが存在する場
合にはpreflightリクエストが発生する。OPTIONSメソッドで
利用可能なヘッダかどうかを確認する。
            ヘッダ
                                                        x-hogeが利用可
           Access-Control-Request-Headers: x-hoge
                                                        能かどうかを聞
                                                           きに行く
            OPTIONSリクエスト(preflightリクエスト)

                      レスポンス
            ヘッダ                                               WEB
 Browser   Access-Control-Allow-Headers: x-hoge
                                                              サー
                                                              バー
                   GETリクエスト                       x-hogeは利用
                                                   OK!と返答

                      レスポンス
S3のCORS
S3はOPTIONSによるpreflightリクエストを正しく解釈する。
許可対象を*で設定した場合、リクエストがあるごとにリク
エストに沿ったAllowed Headerを返す。

              ヘッダ
           Access-Control-Request-Headers: origin, accept, x-hoge

                       OPTIONSリクエスト

              ヘッダ              レスポンス
           Access-Control-Allow-Headers: x-hoge

 Browser      ヘッダ
                                                                    S3
           Access-Control-Request-Headers: origin, accept, x-test

                       OPTIONSリクエスト

              ヘッダ              レスポンス
           Access-Control-Allow-Headers: x-test
CloudFront + S3 通常のCORS
通常のリクエストなのでpreflightリクエストは飛ばないが、
CloudFrontからS3へのリクエストのところでいくつかのカス
タムヘッダがつくので、そこで動作がおかしくなるケースが
ある。
                                                 ヘッダ
            ヘッダ                                Origin: http://test-domain
          Origin: http://test-domain           X-Amz-Cf-Id: xxxx
                                               X-Forwarded-For: xxx

          GETリクエスト                             GETリクエスト

Browser                                CF                                   S3
     たとえば今回のケースでは                           ここで、preflightによる確
     X-Amz-Cf-Idなどをハード                      認なしで飛んできたカスタ
     コーディング的にS3の                            ムヘッダがあるのでCORS
     Allowed Headerに設定する                    とは認識されず、Access-
     と動くこともあるが、動い                           Control-*ヘッダを返せない
     たり動かなかったり。                             ことがある。
CloudFront + S3 カスタムヘッダ付きCORS
Cloud FrontがOPTIONSメソッドを禁止しているので、
preflightに失敗する。


        ヘッダ
       Access-Control-Request-Headers:
       origin, accept, x-hoge


          OPTIONSリクエスト

Browser 403 Forbidden                    CF   S3
CloudFront + EC2でCORS
EC2上にApache等を起動するケース。強制的にAccess-
Control-*ヘッダ群を返してやればうまく動く。


                                                   ヘッダ
            ヘッダ                                 Origin: http://test-domain
          Origin: http://test-domain            X-Amz-Cf-Id: xxxx
                                                X-Forwarded-For: xxx

          GETリクエスト                              GETリクエスト

Browser                                CF                                    EC2
           レスポンス                                   レスポンス
                  ヘッダ
                Access-Control-Allow-Origin: http://test-domain
                Access-Control-Allow-Methods: GET
                Access-Control-Max-Age: 3000
                Access-Control-Allow-Credentials: true
まとめ
• 現状、CloudFront経由でS3のCORSはうまく
  動かないケースがある。これはCORSの
  preflightリクエストの仕様上しようがないお
  話。
• ワークアラウンドとしては、EC2にWEBサー
  バーを立てて、強制的にAccess-Control-
  Allow-Originヘッダ等を返してあげるのがい
  いと思う。

CloudFront経由でのCORS利用