APIGatewayとS3+CloudFront間のCORS問題の解決方法


APIGatewayとS3+CloudFront間のCORS問題の解決方法

WEBアプリを開発していると、CORS(Cross-Origin Resource Sharing)の問題で詰む人も多いのでは?
私もかなりハマったので、私の環境で解決した方法をメモとして残しておきます。

CORSってなに?

ブラウザが https://aaaa.com/ というサイトを表示中に https://bbbb.com/というサイトへのアクセスを制限するというもの。

よく使われる例としては、ブラウザがあるWEBページを表示する際に、APIで情報をとってくるパターン。このWEBページのドメインとAPIのドメインが異なる場合、CORSのでエラーとなってしまいます。

とまぁCORSの説明をしていると日が暮れるので、CORSに関してざっくりでも内容を知っている方向けに対応策を書いていきます。

参考:https://developer.mozilla.org/ja/docs/Web/HTTP/CORS

構成

上記で例として上げたドメインの異なるWebページとAPIの接続をAWS上で実現したいです。WEBページからWebAPIリクエストをして、Web画面上で表示する流れです。

  • WEBページ → S3+CloudFront+React
  • API → APIGateway+PHP

対応した設定内容

CORSの対応ではHTTPヘッダーのやりとりで解消するので、その設定が必要となります。設定する内容は下記の4つ。

  1. APIGateway
  2. PHPでの返却値対応
  3. S3
  4. CloudFront

利用している各種AWSサービスでそれぞれCORSの設定かつPHP側でのソースレベルでの修正が必要でした。私の環境ではこの1と2の設定が重要でした。

手順1, APIGatewayの設定

GETメソッドを例に設定項目を見ていきます。

手順1-1, OPTIONメソッドの設定

まずはOPTIONで「メソッドレスポンス」を選択。

ブラウザに返却するヘッダーを設定します。

Access-Control-Allow-Headers
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Allow-Methods

次に同じくOPTIONの「統合レスポンス」で返却するヘッダーにセットする値を設定します。

Access-Control-Allow-Headers: 'Content-Type,X-Amz-Date,Authorization,X-Amz-Security-Token,X-PINGOTHER,Access-Control-Allow-Origin'
Access-Control-Allow-Origin:'https://example.com'
Access-Control-Allow-Credentials: 'true'
Access-Control-Allow-Methods: 'GET,OPTIONS'

ここで注意点が二点あります。
1. Access-Control-Allow-Originには呼び出し元のドメイン(サブドメインの場合も正確に)
2. Access-Control-Allow-Credentials: 'true'を必ずセット

私が参考にしたサイトでは Access-Control-Allow-Origin:*とセットされていたり、Access-Control-Allow-Credentialsは言及されていないことが多かったですが、私の場合はこの2点がキモでした。

手順1-2, GETメソッドの設定

次はGETにメソッドを設定していきます。

今回はVPCリンクを使っており、「統合リクエスト」でプロキシ統合の使用にチェックを入れていました。

プロキシ統合に関しては下記の記事を参考にしてください。
(参考: API Gateway + Lambda プロキシ結合の使用有無による違い

プロキシ統合の使用っていうのはいわば「API Gateway側でうまく仕事してくれるよ」ってことなんですが、これによって「統合レスポンス」の設定ができなくなるんです。

私はここでハマりました。
プロキシ統合を利用して、APIGateway側でよしなにしてほしい。
=プログラム側では制御できない。

だと思いこんでいましたが、プロキシ統合を利用してもプログラム側でヘッダー情報の付与してもいいみたいでした。

手順2, プログラム側での対応

APIGatewayで設定できないHTTPヘッダーの値をPHP側で設定してあげます。今回はFuelPHPで書いています。

$ret->set_header('Access-Control-Allow-Origin', 'https://example.com');
$ret->set_header('Access-Control-Allow-Credentials', 'true');

これだけでOKです。

要は、手順1-1で行った統合レスポンスの設定をプログラム側でセットしようということです。

そういう意味ではAccess-Control-Allow-HeadersAccess-Control-Allow-Methodsも設定せねばならなそうですが、今の所はこれで動いています。

今回はVPCリンクで使用していますが、LamdaでもHTTPでも同様の設定でいけると思います。もちろんアプリケーション側もNode.jsなどでもOKです。

手順3, S3の設定

ここまでくればあとは流れ作業です!

S3にもCORSの設定を行います。
「S3バケット>アクセス権限>CORSの設定」に下記をそのままコピペでOKです。

AllowOriginはこれまでと同じように許可すべきドメインに変更してください。


<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>https://example.com</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

手順4, CloudFrontの設定

最後にCloudFrontです。

「Distribution > Behaviors > Edit」から設定をします。

  • 「Cache Based on Selected Request Headers」をWhite Listに設定
  • 「Whitelist Headers」でOriginを追加

まとめ

CORSの解決はAWS側の設定だけで大丈夫だという先入観があることでハマってしまった事例かと思います。

自前で設定してあげるというのは少し考えれば至極当然なのですが、、、

CORSはWebを触っていると一度はぶつかる壁だと思います。
僕と同様困っている人の手助けになれば幸いです。