CloudFront+S3でSPAのルーティングをする方法(2021)


はじめに

基本的な構築方法は省略

ルーティング問題

URLを/foo /bar の用に Router系でパス指定した場合、ローカルやNginx等では動いてたのにCloudFrontだと、403エラーになるという問題が起きる。これを回避するために CloudFrontで403エラーが発生したら /index.html に転送するという方法がよく行われている(というか自分もVue Routerで下記などの事例を参考に適用していた)。

なんでも200 OK問題

  • ファビコンをちゃんと用意してないとブラウザーが/favicon.icoを引っ張ってくるが/index.htmlのHTMLが転送される。(まあ、アイコンとして表示されないけど気持ち悪い)
  • 脆弱性検知ツール等がデバッグ用とか管理用のファイルとかのよくあるパスでアクセスしてきて、それに対して200で応答を返すので、そのファイル消したほうが良いって警告される。(まあ、そういうもんだって話ですが、監査を受けると説明しなきゃいけない)
  • foo.png bar.js hage.css とかなんでも/index.htmlのHTMLなのはどうなのか…。

CloudFront Functions

AWS samplesにそれっぽいものがある / -> /index.html/foo/ -> /foo/index.html って書き換えなので、上記のような書き換えだとうまく行かない。

なので、上記をベースに拡張子なし=ルーティングされたもの、と言う判断で書き換えれば良い。

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

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

    return request;
}

Terraform

resource "aws_cloudfront_function" "test" {
  name    = "test"
  runtime = "cloudfront-js-1.0"
  comment = "my function"
  publish = true
  code    = file("${path.module}/function.js")
}
resource "aws_cloudfront_distribution" "example" {
  # 省略
  default_cache_behavior {
    # 省略
    function_association {
      event_type   = "viewer-request"
      function_arn = aws_cloudfront_function.test.arn
    }
  }
  # 省略
}