LoginSignup
5
4

More than 1 year has passed since last update.

SPA+CloudFrontマルチオリジン構成で「/」以外へのアクセスをルーティングする

Last updated at Posted at 2022-04-03

やりたいこと

AWS CloudFront ではリクエストパスのパターンに応じて、1つのドメインから複数のオリジンにアクセスを振り分けることができます。
以下のように、S3 でフロントエンドの SPA を配信し、バックエンドを API Gateway で提供するようなマルチオリジン構成を考えます。
multi-origin.png

SPA を CloudFront + S3 で配信する時の問題点

SPA を CloudFront + S3 で配信する時に問題になるのが、ルートパス ("/") 以外の URL に直接アクセスしたときに 403 エラーが発生するという現象です。
例えば https://example.com/page1 という URL にアクセスしたとき、実際に S3 上に /page1 というオブジェクトが存在しないため、403 エラーが発生します。
403error.jpg

よくある解消法(カスタムエラーレスポンス)

よく紹介されている解消法として、CloudFront でカスタムエラーレスポンスを設定することでこの問題を回避することができます。
custom-error-response.jpg
上記の設定は「オリジンから 403 or 404 が返却されたら、代わりに /index.html に転送するよ」という意味になります。
すなわち https://example.com/page1 にアクセスしても実際には https://example.com/index.html の資材を返却してくれるようになります。

マルチオリジン環境でのカスタムエラーレスポンスの問題点

フロントエンドの配信だけであれば上記の対策方法で問題ないのですが、マルチオリジン構成では1つ困ったことが生じます。
それは「カスタムエラーレスポンスはオリジンごとに設定できない」という点です。

バックエンド側で 403 エラーが発生したとしても、CloudFront ではカスタムエラーレスポンスに従って処理を行なってしまうので、ブラウザには 200 /index.html がレスポンスとして返却されてしまいます。
multiorigin-problem.png
ブラウザ側でエラーハンドリングを実装したくても、CloudFront によってエラーレスポンスが書き換えられてしまうため、機能しません。

解決策

CloudFront Functions を利用します。CloudFront Functions は、CloudFront を通過するあらゆるリクエストとレスポンスに簡単な処理を挟むことができます。
今回は AWS の公式ドキュメントでも紹介されている方法を利用します。

Add index.html to request URLs that don’t include a file name

This function can be useful for single page applications or statically generated websites that are hosted in an Amazon S3 bucket.

CloudFront > Functions から、以下のような Function を作成しましょう。コードは、上記 AWS 公式ドキュメントで紹介されているコードをそのまま貼り付ければ OK です。
Function.jpg

このコードでは、リクエスト URL にファイル名や拡張子を含まない場合、オリジンに対してはその直下の /index.html へアクセスするようにリクエストを書き換えています。

Function 作成後、Publish タブから Publish function を行うことで、CloudFront ディストリビューションと紐づけることが出来るようになります。
CloudFront > Distributions > {Distribution ID} > Behaviours から S3 向けのビヘイビアを編集し、Viewer request に作成した Function を紐付けましょう。
associations.jpg

ここで大事なのは、Function はカスタムエラーレスポンスと違ってディストリビューション毎ではなくビヘイビア毎に設定できる点です。以上の設定により、バックエンドへのリクエストには影響を与えずに、SPA へのルートパス以外へのアクセスを正常に行えるようになりました。

CloudFront Functions を利用する上での Tips

利用していて一番驚いた点は、ランタイム環境です。constlet も使用することができません。

The CloudFront Functions JavaScript runtime environment is compliant with ECMAScript (ES) version 5.1 and also supports some features of ES versions 6 through 9.

その分非常に軽量・高速な処理が可能といった感じです。 Lambda@Edge でも CloudFront Functions と似たようなことが実現できていましたが、環境や制約が異なるため、Lambda@Edge から移植する際には注意が必要です。使い分けについてはこちらを参照しましょう。

独自に Function をカスタマイズしたい場合は、テスト機能を活用しましょう。実際にデプロイを行う前に、リクエストやレスポンスをエミュレートしてテストすることができます。
test-function.jpg

さいごに

CloudFront Functions を利用することで、今回紹介したリクエストパスの書き換え以外にも様々な処理を挟むことができます。公式ドキュメントでも色々な例が紹介されているので、ぜひ参考にしてください。
Customizing at the edge with CloudFront Functions
例えば以下のようなことが簡単に実現できます。

  • CORS ヘッダをリクエストに追加
  • レスポンスにセキュリティヘッダを追加する
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4