REST APIの前段にCloudFrontを配置する
とりあえず以下のような内容でCloudFrontのデストリビューションを作成。

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

許可された HTTP メソッド は、REST APIが取りうるHTTPメソッドをすべて含む必要があるので、全部入りを選択。
また、キャッシュポリシーの指定が必須だったので一旦なんとなく CachingOptimized に。
これら以外はすべてデフォルトのまま。

一旦デフォルトのまま。

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

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

このディストリビューションドメイン名を、REST APIのクライアント(今回はSPAのフロントエンド)から my-rest-api.com の代わりに使うようにした。
が、この時点ではSPAはさっぱり期待どおり動かず。
ブラウザを見るとCORSエラーが出ていたので、
この辺りを参考に、
- キャッシュポリシーに
Originヘッダーを含めるように - オリジンリクエストポリシーを(とにかく全部転送してもらって構わないので)
AllViewerに
した。

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

ここをこんな感じに。
これでひとまずCORSエラーは出なくなった。
と思ったけど、なんかSPAを繰り返し操作してたら突然Vercel上でaxiosが
SyntaxError: Unexpected token < in JSON at position 0
で死んでVercelが500になる現象が発生。
このSPAはフロントエンドがNext.js製でVercelにデプロイされている
あれこれ試行錯誤した結果、オリジンリクエストポリシーを 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エラーになった。
この辺りから、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-SiteSec-Fetch-ModeSec-Fetch-DestAccept

見たもの
その他、ここまでに見たものメモ
一点見落としがあった。
バックエンドのREST APIに、ファイルアップロードのための multipart/form-data なエンドポイントがあって、そこへのリクエストが403になる状態だった。
原因がさっぱり分からなかったけど、ひたすら設定いじって試行錯誤したりググりまくったりしてたら
これを見つけて、WAFによるものだと判明した。
上記記事を参考に、WAFの設定で SizeRestrictions_BODY を Override to Allow にすることでひとまず動作するようになった。

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