📲

CloudFrontでURLにインデックスを自動で付ける

2022/02/14に公開3

こんにちは,@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レンタルサーバーのインデックス優先順位
お名前.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準拠という点ですね。letconstは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のサイドバーにある関数を開きます
CloudFrontのサイドバー

2. 関数を作成

関数を作成します。名前と説明を入力します。説明はいつも通り省略可です。
CloudFront Functionの作成

上のコードをコピー・ペーストします。
CloudFront Functionコードの入力

3. 発行・関連付け

関数を発行します。発行タブに移動して,関数を発行をクリックします。
CloudFront Functionの発行

その後,ディストリビューションと関連付けを設定します。
CloudFront Functionの関連付け1
CloudFront Functionの関連付け2

これで完成です!操作自体はとても簡単ですね。

まとめ

更新したディストリビューションはすぐに反映されます。最後にwgetコマンドで確認してみましょう。
wgetコマンドの結果

ディレクトリ名でリクエストしてもきちんとindex.htmlにアクセスすることができました。
S3+CloudFrontでホスティングするときにぜひ使ってみてください!

脚注
  1. AWSは自由が利く分マニュアル運転に近いと感じています。例えば,サブネットのプライべート・パブリックの設定一つとってもそうです。ちなみに筆者は若者には珍しく(?)マニュアルで免許を取りました。 ↩︎

  2. CloudFrontには計13のエッジリージョンと計300以上のエッジロケーションがあります。凄まじいですね↩︎

  3. 最初それを知らないでconstを使って書いたら怒られました。 ↩︎

GitHubで編集を提案

Discussion

jkobaxjkobax

ご存知かもしれませんが、CloudFrontのディストリビューションの直下だけはデフォルトルートオブジェクトを指定できるんですよね。
index.html を簡単に補えます。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/DefaultRootObject.html

ただ、サブディレクトリには有効にならない罠あってこの記事にある通り書き換えないといけない罠。
このあたりはニーズ多いでしょうし、CloudFrontの機能に組み込んでもらいたいところですね。

ry_kmry_km

コメントありがとうございます!
おっしゃる通り,ディストリビューションの直下だけは対応してくれるのですが,サブディレクトリに入ると403が返ってくるんですよね...(一応インデックスの設定についての真ん中あたりに同じリンクも貼ってあります!)
地味に不便なので対応してほしいですよね。

jkobaxjkobax

あ、ルートのところですね。見逃し失礼。
最近わたしもこの設定を試したところだったので思わずコメントしちゃいました。