Closed10

REST APIの前段にCloudFrontを配置する

たつきちたつきち

とりあえず以下のような内容でCloudFrontのデストリビューションを作成。

オリジンドメインを入力した以外はすべてデフォルトのまま。


許可された HTTP メソッド は、REST APIが取りうるHTTPメソッドをすべて含む必要があるので、全部入りを選択。

また、キャッシュポリシーの指定が必須だったので一旦なんとなく CachingOptimized に。

これら以外はすべてデフォルトのまま。


一旦デフォルトのまま。


とりあえずあって困るものでもなさそうなのでWAFは有効に。「有効にする」を選択した以外はデフォルトのまま。


日本国内からのアクセスがメインのサイトなので、料金クラスは 北米、欧州、アジア、中東、アフリカを使用 を選択。それ以外はすべてデフォルトのまま。

たつきちたつきち

作成後、

このディストリビューションドメイン名を、REST APIのクライアント(今回はSPAのフロントエンド)から my-rest-api.com の代わりに使うようにした。

が、この時点ではSPAはさっぱり期待どおり動かず。

たつきちたつきち

ブラウザを見るとCORSエラーが出ていたので、

https://matsu.teraren.com/blog/2022/12/14/cloudfront-cors-s3/

https://zenn.dev/bun913/articles/cloudfront-cors-policies

この辺りを参考に、

  • キャッシュポリシーに Origin ヘッダーを含めるように
  • オリジンリクエストポリシーを(とにかく全部転送してもらって構わないので)AllViewer

した。

こんなキャッシュポリシーを作った上で、ビヘイビアの編集画面で

ここをこんな感じに。

これでひとまずCORSエラーは出なくなった。

たつきちたつきち

と思ったけど、なんかSPAを繰り返し操作してたら突然Vercel上でaxiosが

SyntaxError: Unexpected token < in JSON at position 0

で死んでVercelが500になる現象が発生。

このSPAはフロントエンドがNext.js製でVercelにデプロイされている

https://twitter.com/ttskch/status/1699303851827081342

あれこれ試行錯誤した結果、オリジンリクエストポリシーを AllViewer にしているとこの現象が発生することが判明。理屈は謎。

バックエンドのCORS設定は

Access-Control-Allow-Headers: Content-Type, Authorization

だったので、以下のように Content-Type だけを含むオリジンリクエストポリシーを作成。

Authorization ヘッダーはオリジンリクエストポリシーに含めることはできず、キャッシュポリシーによってオリジンへ転送する仕様らしいので、Content-Type のみでOK(のはず)

こうしたら謎現象は発生しなくなった。


これ、フロントエンドの自分のコードでレスポンスボディを JSON.parse() してるところがあって、CloudFrontから直接エラーレスポンスが返ってくる際にJSONではなくHTMLが返ってくるので、その場合に JSON.parse() が例外を吐いていた。

CloudFrontからは502 Bad Gatewayが返ってきていた。


肝心の、なぜ AllViewer だとダメなのかの理由はさっぱり理解してません。これを見た詳しい方、ぜひコメントで教えてください🙇‍♂️

たつきちたつきち

SPAでログインの操作をしたところ、認証付きのエンドポイントへのpreflight(OPTIONS)リクエストが403になり、fetch(GET)リクエストがCORSエラーになった。

https://dev.classmethod.jp/articles/how-do-i-enable-basic-authentication-behind-cloudfront/

https://repost.aws/ja/knowledge-center/cloudfront-authorization-header

この辺りから、Authorization ヘッダーをオリジンに転送するようにし、Authorization ヘッダーをキャッシュキーにも含めるようにすればよさそうという理屈が理解できた。

ので、まずは以下のようにキャッシュポリシーを編集してキャッシュキーに Authorization ヘッダーを追加した。これによって Authorization ヘッダーがオリジンに転送されるようにもなる。

さらに以下のようなレスポンスヘッダーポリシーを作成してセットした。

すると、認証付きのエンドポイントへのpreflight(OPTIONS)リクエストが405(Method not allowed)になってしまった・・・

どうもこれはバックエンド側の実装の問題っぽい予感がするが、まだ原因究明できず・・・

たつきちたつきち

上記、原因が判明し、解決できた。

バックエンド(オリジンたるREST API)のログを確認したところ、Access-Control-Request-Headers ヘッダーおよび Access-Control-Request-Method ヘッダーが送られていないことが分かった。

これらのヘッダーがないリクエストはバックエンドから見るとCORS preflightリクエストではないので、通常のアプリのルーティングによって処理されてしまい、「そのエンドポイントはOPTIONSメソッドでのリクエストは許可されてませんよ」で405になっていた。

というわけで、オリジンリクエストポリシーを以下のように修正することで問題が解決できた。

ついでに、CloudFrontを通さなかった場合と通した場合でのバックエンドのログの差分を見て、他にも以下のヘッダーが削除されていることが分かったので、ついでに追加した。

  • Sec-Fetch-Site
  • Sec-Fetch-Mode
  • Sec-Fetch-Dest
  • Accept


見たもの

https://zenn.dev/hunbari_front/articles/58e081b487093e

https://dev.classmethod.jp/articles/how-to-put-api-gateway-with-custom-authorization-bihind-cloudfront/

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html#request-custom-headers-behavior

たつきちたつきち

一点見落としがあった。

バックエンドのREST APIに、ファイルアップロードのための multipart/form-data なエンドポイントがあって、そこへのリクエストが403になる状態だった。

原因がさっぱり分からなかったけど、ひたすら設定いじって試行錯誤したりググりまくったりしてたら

https://stackoverflow.com/questions/64853122/aws-waf-getting-403-forbidden-error-while-trying-to-upload-an-image

これを見つけて、WAFによるものだと判明した。

上記記事を参考に、WAFの設定で SizeRestrictions_BODYOverride to Allow にすることでひとまず動作するようになった。

たつきちたつきち

あとは動的コンテンツのInvalidateなどの対応が必要だけど、それはバックエンドのアプリレイヤーで対応する話なのでこのスクラップはここまででクローズ🍵

このスクラップは2023/09/07にクローズされました