🔪

CloudFront Functionsでhugoサイトのindex.htmlを補完する

2023/06/21に公開

はじめに

静的サイトジェネレータのHugoを使っています。
HugoをCloudFront + S3の構成で動かしアクセスした場合、ルートページ以外にアクセス出来ません。
というのもリンクを押すと以下のようなURLに遷移するからです。(一例です)

https://<domain>/posts/<ページ名>/

はい、index.htmlが指定されていません。
そのためアクセスができないというわけです。

今回は

https://<domain>/posts/<ページ名>/index.html

自動的にこのようなURLへ補完してくれるよう、設定を追加したいと思います。
プロビジョニングはTerraformを使用します。

ゴール

index.htmlを入力せずにアクセスできたらOKです。

CloudFront Functions未設定

$ curl -I https://<domain>/posts/<ページ名>/
HTTP/2 403
server: AmazonS3

CloudFront Functions設定後

$ curl -I https://<domain>/posts/<ページ名>/
HTTP/2 200 
server: AmazonS3

環境

terraform 1.4.6

方針

CloudFront Functionsを使用することでディストリビューションをカスタマイズすることが出来ます。
こいつの機能を使用してindex.htmlを補完したいと思います。

CloudFront Functionsとはなに?

コードをCloudFront Distributionに紐付け、それを各エッジロケーションで動作させる実行環境のことです。
cloudfrontを経由するリクエストやレスポンスの操作が可能です。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html
https://dev.classmethod.jp/articles/amazon-cloudfront-functions-release/
https://www.infiniteloop.co.jp/tech-blog/2022/12/sounds-good-cloudfront-functions-use-case/

似たようなサービスであるLambda@Edgeとの違いはこちらに記載があります。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/edge-functions.html

無料枠があるのはいいですね!
あとさばけるリクエスト数もCloudfrontFunctionsに軍配があがるようです。

使用する関数(コード)

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;
}

まんまドキュメントにあるため、そのまま使用します。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/example-function-add-index.html

検証

慣れていないため、手動作成とTerraformでの作成を両方試したいと思います。

手動で試す

ここを見ればOKです。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/functions-tutorial.html

上記の関数を保存後、

  • [Publish] タブを選択し、[Publish] ボタンを選択して関数を公開
    • cloudfrontディストリビューションに紐付ける前に公開する必要あり
  • Associated distributionsから
    • 紐づけたいcloudfrontディストリビューション
    • Event TypeとしてViewer Request
    • Cache behaviorでデフォルト
  • ディストリビューションのステータスが [Deployed] になったら動作確認を行う

のように操作します。
動作確認を行い、URLが補完されればOKです。

環境の削除

Terraformでも試すため、削除します。

  • cloudfrontディストリビューションとの関連付けを解除
  • ディストリビューションへの適用をしばらく待つ

のようにすればOKです。

Terraformで試す

コードは別ファイルで管理して、fileで参照するようにします。

resource "aws_cloudfront_function" "add-index-function" {
  name    = "add-index-function"
  runtime = "cloudfront-js-1.0"
  comment = "Add index.html to the path"
  publish = true
  code    = file("${path.module}/addIndexFunction.js")
}

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_function

addIndexFunction.js
function handler(event) {
  var request = event.request;
  var uri = request.uri;

  // Check whether the URI is missing a file name.
  if (uri.endsWith("/")) {
    request.uri += "index.html";
  }
  // Check whether the URI is missing a file extension.
  else if (!uri.includes(".")) {
    request.uri += "/index.html";
  }

  return request;
}

最後にディストリビューション側で指定する必要があります。
忘れずに行いましょう。

resource "aws_cloudfront_distribution" "hugo_cfront" {
// 略
    function_association {
      event_type   = "viewer-request"
      function_arn = aws_cloudfront_function.add-index-function.arn
    }
  }

あとはplan,applyで設定適用まで待てばOKです。

動作確認

$ curl -I https://<domain>/posts/<ページ名>/
HTTP/2 200 
server: AmazonS3

このように、index.htmlを指定せずともアクセスできればOKです!

最後に

AWSドキュメントにそのまま答えが書いてあったので楽でした。
ログ設定などは出来ていないため、必要に応じて検討していきます。

参考

https://blog.nns7.work/post/publish-hugo-blog-using-cloudfront-and-s3/

Discussion