CloudFront のサブドメインを S3 の同じ名前にマップするにはどうすればよいですか?

CloudFront のサブドメインを S3 の同じ名前にマップするにはどうすればよいですか?

私は次のことを行う方法を探していました。もし誰かが私に教えてくれたら大変ありがたいです。

たとえば、単一の CloudFront インスタンスで次のマッピングは可能でしょうか?

feature-a.domain.com => dev-bucket/feature-a
feature-b.domain.com => dev-bucket/feature-b
staging.domain.com   => dev-bucket/staging

などなど。

ユースケースとしては、存在する S3 上のバケットにマップされる各 Git ブランチに対して、できるだけ多くの環境をデプロイできるようにしたいです。これはすべて可能でしょうか?

答え1

はい、可能です。ただし、リクエストをバケットに送信する前にリクエストパラメータを操作するには、CloudFront の Lambda@Edge 拡張機能を使用する必要があります。

私は一つの解決策を説明した。この公式フォーラム投稿同じ解決策が以下に示されています。

Lambda@Edge は、CloudFront が HTTP リクエストまたはレスポンスを処理しているときに、プログラムによるアクセスを許可します (基本的にはトリガー フックを提供します)。HTTP トランザクションは JavaScript オブジェクトとして提示され、監視および変更してから CloudFront に制御を返すことができます。

このアプリケーションでは、バケットの Web サイト ホスティング エンドポイントを CloudFront 構成のオリジン ドメイン名として使用する必要があります (つまり、ドロップダウンからバケットを選択せず​​、適切な「s3-website」ホスト名を使用して入力します)。また、ヘッダーをHostオリジンに転送するためにホワイトリストに登録する必要があります。ただし、実際には転送しません (パスとともに読み取り、操作します)。通常、これを S3 オリジンに転送しても意図したとおりに動作しませんが、読み取りと操作ができるように、CloudFront にホワイトリストに登録するように指示する必要があります。

次の Lambda 関数を Origin Request トリガーとして設定します。このトリガーは、CloudFront キャッシュがチェックされ、キャッシュミスが発生した後、リクエストがオリジンサーバー (S3) に送信される前に実行されます。

'use strict';

// https://serverfault.com/a/930191/153161
// if the end of incoming Host header matches this string, 
// strip this part and prepend the remaining characters onto the request path,
// along with a new leading slash (otherwise, the request will be handled
// with an unmodified path, at the root of the bucket)

// set this to what we should remove from the incoming hostname, including the leading dot.

const remove_suffix = '.example.com';

// provide the correct origin hostname here so that we send the correct 
// Host header to the S3 website endpoint
// this is the same value as "origin domain name" in the cloudfront distribution configuration

const origin_hostname = 'example-bucket.s3-website-us-east-1.amazonaws.com';

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  const headers = request.headers;
  const host_header = headers.host[0].value;

  if(host_header.endsWith(remove_suffix))
  {
    // prepend '/' + the subdomain onto the existing request path ("uri")
    request.uri = '/' + host_header.substring(0,host_header.length - remove_suffix.length) + request.uri;
  }

  // fix the host header so that S3 understands the request
  // we have to do this even if the above if() didn't match
  headers.host[0].value = origin_hostname;

  // return control to CloudFront with the modified request
  return callback(null,request);
};

また、最小 TTL を 0 にキャッシュするエラーCloudFront のキャッシュエラー応答を回避するためです。これは、キャッシュ動作設定の min/default/max TTL とは別の設定です。デフォルトは 5 分で、これは理にかなっています。ただし、この動作を予期していない場合はトラブルシューティングが複雑になります。


Lambda@Edge関数はv6.10またはバージョン8.10Node.js ランタイム環境。上記の例は元々は v6.10 用に作成されましたが、v8.10 ではハンドラーにさらに多くのオプションがあるため、どちらのランタイムとも互換性があります。async/await を使用したり、promise を直接返したり、上記のように記述してコールバック インターフェイスを使用したりできます。

関連情報