CloudFront ~ S3 で CORS を設定する

CloudFront 経由で公開している S3 に Web フォントを置いて、それをクロスオリジンで利用できるようにするために Access-Control-Allow-Origin ヘッダーを返すように設定したときのメモ。

以下の 2 通りの方法が考えられるでしょうか

  • CloudFront のレスポンスヘッダーポリシーで設定
  • S3 で CORS を設定して CloudFront から S3 へ Origin を渡す

CloudFront のレスポンスヘッダーポリシーで設定

CloudFront のビヘイビアで次のようなレスポンスヘッダーポリシーを設定します。

resource "aws_cloudfront_response_headers_policy" "cors" {
  name    = "example-cors"
  comment = "example-cors"
  cors_config {
    access_control_allow_methods {
      items = ["GET"]
    }
    access_control_allow_origins {
      items = ["example.com"]
    }
    access_control_allow_headers {
      items = ["*"]
    }
    access_control_allow_credentials = false
    origin_override                  = true
  }
}

resource "aws_cloudfront_distribution" "main" {
  // ...snip....

    response_headers_policy_id = aws_cloudfront_response_headers_policy.cors.id

  // ...snip....
}

次のように origin ヘッダーを渡してやれば access-control-allow-origin が返ってきます。

curl -I -XGET https://xxx.cloudfront.net/example.woff -H origin:example.com | grep access-control-allow-origin
#=> access-control-allow-origin: example.com

任意の origin を許可して OK なら、カスタムレスポンスヘッダーポリシーを用意しなくても SimpleCORS などのマネージドポリシーでも良いと思います。

data "aws_cloudfront_response_headers_policy" "cors" {
  name = "Managed-SimpleCORS"
}

S3 で CORS を設定して CloudFront から S3 へ Origin を渡す

S3 で次のように CORS を設定します。

resource "aws_s3_bucket_cors_configuration" "main" {
  bucket = aws_s3_bucket.main.id
  cors_rule {
    allowed_methods = ["GET"]
    allowed_origins = ["example.com"]
  }
}

そのうえで CloudFront のキャッシュポリシーで origin をキャッシュキーに含めます

resource "aws_cloudfront_cache_policy" "origin" {
  name    = "example-origin"
  comment = "example-origin"

  min_ttl     = 1
  max_ttl     = 31536000
  default_ttl = 86400

  parameters_in_cache_key_and_forwarded_to_origin {
    headers_config {
      header_behavior = "whitelist"
      headers {
        items = ["origin"]
      }
    }
    cookies_config {
      cookie_behavior = "none"
    }
    query_strings_config {
      query_string_behavior = "none"
    }
  }
}

resource "aws_cloudfront_distribution" "main" {
  // ...snip....

    cache_policy_id = aws_cloudfront_cache_policy.origin.id

  // ...snip....
}

次のように origin ヘッダーを渡してやれば access-control-allow-origin が返ってきます。

curl -I -XGET https://xxx.cloudfront.net/example.woff -H origin:example.com | grep access-control-allow-origin
#=> access-control-allow-origin: example.com

なお、キャッシュポリシーのキャッシュキーに origin を含めなくても、オリジンリクエストポリシーに origin を含めれば origin 付きのリクエストに対して access-control-allow-origin が返るようになります。ただその場合、リクエストの origin が違っていてもキャッシュが共通になるため、origin が無かったり違ったりするリクエストを元に access-control-allow-origin が無いレスポンスがキャッシュされてしまうと、正しい origin を送ったとしてもキャッシュから access-control-allow-origin が無いレスポンスが返されてしまうため、ダメです。

さいごに

CloudFront のレスポンスヘッダーポリシーで設定する方が、CloudFront がリクエストの origin を見て共通のキャッシュから access-control-allow-origin を出しわけてくれるので、こちらの方が良いですね。

S3 で CORS を使うのは CloudFront でレガシーキャッシュ設定しか使えなかった頃の名残で、もう使うことは無さそうです(CloudFront を噛まさない裸の S3 なら別ですけど)。