CloudFrontでURLにインデックスを自動で付ける
こんにちは,@ry_kmです。Zenn2記事目です。
みなさんAWSのS3+CloudFront使ってますか?
簡単に低コストでホームページの運用ができるサービスで,SPA(Single-Page Application)をホスティングするのに大変重宝しています。
ただ初期設定こそ簡単ですが,運用中につまづくポイントが多々あります。
今回は,その中でもあまり知られていないインデックスの設定方法を紹介します。
S3+CloudFrontのWebホスティングとは?
S3はAWSの代表的なストレージサービス,CloudFrontは同じくAWS謹製のCDNです。S3には静的Webホスティングという機能があり,サーバーサイドプログラムを必要としないWebページ(PHPやASP.NETなどを使わないページ)のホストが簡単にできます。
S3のバケットはアクセス権(ポリシー)を自由に設定することができるので,「作成者は変更・読み取り許可,その他は読み取り専用」とポリシーを設定することで静的ホスティングが可能です。
メリット
- ApacheやNginxなどの細かい設定をする必要がない
- サーバーレスでの運用が可能(マネージドサービスなので利用料が安い)
- データの耐久性が非常に高い
CloudFrontとの組み合わせ
また,CloudFrontと組み合わせることで以下のことができるようになります。
- HTTPS化(ACLで証明書を発行する必要あり)
- キャッシュを取って読み込みを高速化
- S3バケットに直接アクセスさせずにホスティングが可能(CloudFrontを使って間接的にバケットにアクセスする)なため,セキュリティー的に安心
最近はhttpsでないとSEOのランキングが下がるらしいので,ホームページのHTTPS化は必須ですね。したがって,基本的にS3とCloudFrontは組み合わせて使う必要があります。
インデックスの設定について
一方で大抵のレンタルサーバーサービスにあって,S3+CloudFrontにはない機能があります。その一つが「インデックスの指定」です。
例えば,お名前.comのレンタルサーバーには次のような記載があります。
お名前.comレンタルサーバーのインデックス優先順位。出典:https://help.onamae.com/answer/20292
このように,レンタルサーバーではデフォルトでindex.htmlを参照するように設定されているわけですね(Apacheの内部設定で可能)。つまり,example.com
にアクセスすると自動的にexample.com/index.html
にリダイレクトされるようになっています。
しかしながら,マニュアル運転のAWS[1]ではそうもいきません。S3+CloudFrontでホスティングしている場合,インデックスの設定に関して以下の制限があります。
- バケットのルートにアクセスしている場合,末尾の
index.html
は省略可能(設定方法) - バケットのルート以外にアクセスしている場合,末尾の
index.html
は省略不可
したがって,以下のようなアクセスでは403エラーが出ます。
- アクセス先:
s3://example-bucket/hoge/index.html
-
example.com
に紐づけたバケット:s3://example-bucket/
- アクセスしたリンク:
example.com/hoge/
wget
コマンドを使うとこのようになります。
デフォルトの設定でインデックスを省略した場合。/api
にはindex.htmlが入れてあります。
しかしこれが使いにくい...いちいちindex.htmlまで打たないといけないので,かなりメンドクサイです!
これを解決するために,CloudFront Functionsを使ってリクエストのパスを書き換えるということをします。
CloudFront Functionsとは?
CloudFront Functions(以下CF2と書きます)は,2021/5/6にローンチされた比較的新しいサービスです。
カスタマイズされたエクスペリエンスを可能な限り最小のレイテンシーで提供するために、今日の多くのアプリケーションはエッジで何らかの形式のロジックを実行します。
この(...)ユースケースを支援するために、218 以上の CloudFront エッジロケーションで軽量の JavaScript コードを Lambda@Edge の 1/6 のコストで実行できる新しいサーバーレススクリプトプラットフォームである CloudFront Functions の提供が開始されました。
(引用:CloudFront Functions の導入 – 任意の規模において低レイテンシーでコードをエッジで実行)
似たようなサービスにLambda@Edgeがありますが,CF2には以下の特徴があります。
- ランタイム:JavaScriptのみ(ECMAScript5.1準拠)
- 実行場所:218のCloudFrontエッジロケーション[2]
- 実行時間:1msまで
- 使用可能メモリ:2MBまで
- パッケージサイズ:10KBまで
- ビューアリクエスト・レスポンスのみペイロードとして与えられる
- VPC・ファイルシステムへのアクセスは不可
エッジロケーションで実行できるというのが激アツです!1ms以内という制限があるので,使用用途はパスの書き換えやリダイレクトなどかなり絞られると思います。
注意が必要なのは,JSとはいってもECMAScript5.1準拠という点ですね。let
やconst
はECMAScript6 (2015)で導入されたので,var
しか使用できません[3]。アロー関数(const hoge = () => {}
)も使えません。
実際にやってみる
使うコードは以下の通りです。
function handler(event) {
var request = event.request
var uri = request.uri;
if (uri.endsWith('/')) {
request.uri += 'index.html';
} else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}
今回はAWS公式のコードをお借りしました。これをCF2に設定していきます。
1. CloudFront Functionsを開く
CloudFrontのサイドバーにある関数を開きます
2. 関数を作成
関数を作成します。名前と説明を入力します。説明はいつも通り省略可です。
上のコードをコピー・ペーストします。
3. 発行・関連付け
関数を発行します。発行タブに移動して,関数を発行をクリックします。
その後,ディストリビューションと関連付けを設定します。
これで完成です!操作自体はとても簡単ですね。
まとめ
更新したディストリビューションはすぐに反映されます。最後にwget
コマンドで確認してみましょう。
ディレクトリ名でリクエストしてもきちんとindex.html
にアクセスすることができました。
S3+CloudFrontでホスティングするときにぜひ使ってみてください!
-
AWSは自由が利く分マニュアル運転に近いと感じています。例えば,サブネットのプライべート・パブリックの設定一つとってもそうです。ちなみに筆者は若者には珍しく(?)マニュアルで免許を取りました。 ↩︎
-
最初それを知らないで
const
を使って書いたら怒られました。 ↩︎
Discussion
ご存知かもしれませんが、CloudFrontのディストリビューションの直下だけはデフォルトルートオブジェクトを指定できるんですよね。
index.html を簡単に補えます。
ただ、サブディレクトリには有効にならない罠あってこの記事にある通り書き換えないといけない罠。
このあたりはニーズ多いでしょうし、CloudFrontの機能に組み込んでもらいたいところですね。
コメントありがとうございます!
おっしゃる通り,ディストリビューションの直下だけは対応してくれるのですが,サブディレクトリに入ると403が返ってくるんですよね...(一応インデックスの設定についての真ん中あたりに同じリンクも貼ってあります!)
地味に不便なので対応してほしいですよね。
あ、ルートのところですね。見逃し失礼。
最近わたしもこの設定を試したところだったので思わずコメントしちゃいました。