年三日坊主のKKです。
この記事は2026年7月に書いているのですが、拙僧は8月のお盆参りに耐えられたのでしょうか…
はじめに
前回の記事では、CloudFrontマルチテナントディストリビューションの仕組みと、標準ディストリビューションとの違いを解説しました。
今回は、1つのS3バケットを複数のランディングページで共有し、FQDNごとに異なるディレクトリのコンテンツを配信する構成を作成します。
マルチテナントディストリビューションのオリジンパスは、次のように設定します。(以降、頻出する{}は本来は半角文字です)
| /html/{{fqdn}} |
そして、各ディストリビューションテナントに、サイトのFQDNをパラメータとして設定します。
| テナントA: Domain = lp-a.example.com fqdn = lp-a.example.com テナントB: Domain = campaign.example.net fqdn = campaign.example.net |
これにより、次のような振り分けを実現します。
| https://lp-a.example.com/ ↓ s3://example-lp-origin/html/lp-a.example.com/index.html https://campaign.example.net/ ↓ s3://example-lp-origin/html/campaign.example.net/index.html |
今回作成する構成
全体構成は次のとおりです。
| +--------------------------+ | 利用者 | +--------------------------+ │ ├─ https://lp-a.example.com/ │ └─ https://campaign.example.net/ │ ▼ +--------------------------------------+ | DNS | | Route 53 Alias または CNAME | +--------------------------------------+ │ ▼ +--------------------------------------+ | CloudFront 接続グループ | | d111111abcdef8.cloudfront.net | +--------------------------------------+ │ ├─ テナントA │ Domain: lp-a.example.com │ fqdn: lp-a.example.com │ └─ テナントB Domain: campaign.example.net fqdn: campaign.example.net │ ▼ +--------------------------------------+ | マルチテナントディストリビューション | | Origin: S3 | | Origin path: /html/{{fqdn}} | | Default root object: index.html | | OAC: 有効 | +--------------------------------------+ │ ▼ +--------------------------------------+ | S3バケット | | html/ | | ├─ lp-a.example.com/ | | └─ campaign.example.net/ | +--------------------------------------+ |
今回の例では、次のリソース名を使用します。
| 項目 | 設定例 |
|---|---|
| AWSアカウントID | 123456789012 |
| S3バケット | example-lp-origin-123456789012 |
| マルチテナントディストリビューション名 | shared-lp-distribution |
| パラメータ名 | fqdn |
| テナントA | lp-a-example-com |
| テナントB | campaign-example-net |
| FQDN A | lp-a.example.com |
| FQDN B | campaign.example.net |
{{fqdn}}パラメータはリクエストのHostから自動設定されない
最初に、重要な点を確認しておきます。
オリジンパスに記載した次の値は、CloudFrontがリクエストのHostヘッダーから自動的に作成する変数ではありません。
| {{fqdn}} |
これは、マルチテナントディストリビューションで定義し、ディストリビューションテナントの作成時に値を設定する静的なパラメータです。
したがって、今回のようにFQDNごとに異なるS3プレフィックスを参照する構成では、基本的にFQDNごとに1つのディストリビューションテナントを作成します。
次のようなワイルドカードドメインを1つのテナントへ登録するだけでは、{{fqdn}}パラメータをリクエストごとに切り替えることはできません。
| *.example.com |
ワイルドカードを使った1つのプール型テナントは、複数のサブドメインが同じコンテンツや同じオリジンパスを利用する場合には有効です。しかし、今回の構成ではサイトごとにパラメータ値が異なるため、テナントも分けます。
手順1:S3バケットを作成する
最初に、LPのコンテンツを格納するS3バケットを作成します。
| example-lp-origin-123456789012 |
S3バケットでは、次の設定を推奨します。
- S3静的Webサイトホスティング:無効
- パブリックアクセスのブロック:すべて有効
- Object Ownership:Bucket owner enforced
- バケットのバージョニング:有効
- CloudFrontからのアクセス:OACを使用
OACを使用する場合、S3のWebサイトエンドポイントではなく、通常のS3バケットオリジンを指定します。S3 Webサイトエンドポイントはカスタムオリジンとして扱われ、OACまたはOAIを使用できません。
手順2:FQDNごとのディレクトリを作成する
S3内では、次の構造でコンテンツを配置します。
| s3://example-lp-origin-123456789012/ └── html/ ├── lp-a.example.com/ │ ├── index.html │ ├── favicon.ico │ ├── images/ │ │ └── main.webp │ └── assets/ │ ├── app.a1b2c3.css │ └── app.d4e5f6.js │ └── campaign.example.net/ ├── index.html ├── favicon.ico ├── images/ │ └── main.webp └── assets/ ├── app.112233.css └── app.445566.js |
このとき、CloudFrontからS3へのパスは次のようになります。
| ユーザーからのリクエスト | テナントのOrigin path | S3オブジェクトキー |
|---|---|---|
/ |
/html/lp-a.example.com |
html/lp-a.example.com/index.html |
/favicon.ico |
/html/lp-a.example.com |
html/lp-a.example.com/favicon.ico |
/assets/app.a1b2c3.css |
/html/lp-a.example.com |
html/lp-a.example.com/assets/app.a1b2c3.css |
手順3:サーバー証明書を準備する
CloudFrontでカスタムドメインを使用する場合、証明書はバージニア北部リージョン(us-east-1 )に作成します。
今回の例では各サイトの親ドメインが異なります。
| lp-a.example.com campaign.example.net |
そのため、テナントごとに証明書を設定します。
同じ親ドメイン配下だけで運用する場合は、ワイルドカード証明書をマルチテナントディストリビューションに設定し、各テナントへ継承させる方法もあります。
手順4:マルチテナントディストリビューションを作成する
AWSマネジメントコンソールでCloudFrontを開き、次の順に操作します。
| CloudFront → ディストリビューション → ディストリビューションを作成 → マルチテナントアーキテクチャ |
設定例は次のとおりです。
| 設定項目 | 設定値 |
|---|---|
| ディストリビューション名 | shared-lp-distribution |
| アーキテクチャ | マルチテナント |
| オリジンタイプ | Amazon S3 |
| オリジン | example-lp-origin-123456789012 |
| オリジンアクセス | Origin Access Control |
| OAC署名動作 | Sign requests |
| オリジンパス | /html/ |
fqdnパラメータ |
必須 |
| ビューワープロトコルポリシー | Redirect HTTP to HTTPS |
| 許可メソッド | GET、HEAD |
| 圧縮 | 有効 |
| デフォルトルートオブジェクト | index.html |
| キャッシュポリシー | CachingOptimized、またはカスタム |
| AWS WAF | 必要に応じて有効 |
マルチテナントディストリビューションの作成画面では、オリジンドメイン名またはオリジンパスへパラメータを挿入できます。パラメータを必須にすると、各ディストリビューションテナントの作成時に値の入力が必要になります。
Origin pathを設定する
Origin pathには次の値を設定します。
| /html/{{fqdn}} |
fqdnパラメータは「必須」とします。
この設定により、テナントごとに次のように解決されます。
| テナントA: /html/{{fqdn}} ↓ /html/lp-a.example.com テナントB: /html/{{fqdn}} ↓ /html/campaign.example.net |
OACを設定する
OACの署名動作には、AWSが推奨する次の設定を選択します。
| Sign requests |
APIやCloudFormation上では、次の値に相当します。
| always |
この設定では、CloudFrontからS3への通信が署名され、HTTPSで行われます。
デフォルトルートオブジェクトを設定する
デフォルトルートオブジェクトには次の値を設定します。
| index.html |
先頭のスラッシュは付けません。
これにより、次のリクエストが行われた場合、
| https://lp-a.example.com/ |
CloudFrontは内部的にindex.htmlを要求し、Origin pathと組み合わせて次のS3オブジェクトを取得します。
| https://lp-a.example.com/index.html |
デフォルトルートオブジェクトの値を先頭スラッシュ付きで指定すると、403エラーになる可能性があります。
手順5:S3バケットポリシーを設定する
OACを作成しただけでは、CloudFrontはS3オブジェクトを取得できません。
S3バケットポリシーでCloudFrontサービスプリンシパルに s3:GetObject を許可します。
次の例では、html/ 配下だけを参照可能にしています。
| { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowCloudFrontReadLpContents", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::example-lp-origin-123456789012/html/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1234567890ABC" } } } ] } |
次の値を実際の環境に置き換えます。
| 123456789012 → AWSアカウントID E1234567890ABC → S3オリジンを設定したマルチテナントディストリビューションID |
AWS:SourceArn 条件を付けることで、同じAWSアカウント内の別CloudFrontディストリビューションからのアクセスを防ぎます。
手順6:ディストリビューションテナントを作成する
続いて、FQDNごとにディストリビューションテナントを作成します。
CloudFrontコンソールで次の順に操作します。
| CloudFront → ディストリビューションテナント → テナントの作成 |
または、作成したマルチテナントディストリビューションを開き、次の操作を行います。
| テナントの作成 |
入力値
| 項目 | テナントAの値 | テナントBの値 |
|---|---|---|
| テナント名 | lp-a-example-com |
campaign-example-net |
| テンプレートディストリビューション | shared-lp-distribution |
shared-lp-distribution |
| ドメイン | lp-a.example.com |
campaign.example.net |
| サーバー証明書 | lp-a.example.com を含む証明書 |
campaign.example.net を含む証明書 |
| パラメータ名 | fqdn |
fqdn |
| パラメータ値 | lp-a.example.com |
campaign.example.net |
ドメイン名と{{fqdn}}パラメータ値は同じ値にします。
技術的には異なる値も設定できますが、運用ミスを避けるため、今回の構成では一致を必須とします。
FQDNは小文字へ正規化します。DNS名では大文字と小文字を区別しませんが、S3のオブジェクトキーでは大文字と小文字が区別されるためです。
手順7:DNSレコードを設定する
ディストリビューションテナントの詳細画面から、CloudFrontエンドポイントを確認します。
例:
| d111111abcdef8.cloudfront.net |
複数のテナントが同じ接続グループに所属している場合は、各ドメインのDNSレコードが同じCloudFrontエンドポイントを参照します。
手順8:動作確認する
DNS設定とCloudFrontのデプロイが完了したら、動作を確認します。
DNSの確認
| dig +short lp-a.example.com dig +short campaign.example.net |
HTTPレスポンスの確認
| curl -I https://lp-a.example.com/ curl -I https://campaign.example.net/ |
次のようなレスポンスになれば、CloudFront経由で配信されています。
| HTTP/2 200 content-type: text/html via: 1.1 xxxxxxxxx.cloudfront.net (CloudFront) x-cache: Miss from cloudfront |
2回目以降は、キャッシュ状況に応じて次のように表示されます。
| x-cache: Hit from cloudfront |
サイトごとのコンテンツを確認する
各index.htmlに確認用の文字列を入れておくと、振り分けを確認しやすくなります。
S3への直接アクセスが拒否されることを確認する
| curl -I https://example-lp-origin-123456789012.s3.ap-northeast-1.amazonaws.com/html/lp-a.example.com/index.html |
S3バケットを公開していないため、CloudFrontを経由しないアクセスは拒否されます 。
| HTTP/1.1 403 Forbidden |
まとめ
今回は、1つのS3バケット内にFQDNごとのディレクトリを作成し、CloudFrontマルチテナントディストリビューションから複数のLPを配信しました。
構成の中心となる設定は次のとおりです。
| S3: html/<FQDN>/index.html CloudFront Origin path: /html/{{fqdn}} Distribution tenant: Domain = <FQDN> fqdn = <FQDN> |
この構成には、次の利点があります。
- CloudFrontの設定をLP間で共通化できる
- LP追加時はテナントとS3ディレクトリ、必要に応じてサーバー証明書を追加すればよい
- S3バケットを公開せずOACで保護できる
- FQDNごとに証明書やWAFを変更できる
- テナント単位でキャッシュを無効化できる
- AWS CLIやIaCでオンボーディングを自動化しやすい
{{fqdn}}はHost名から自動生成される変数ではなく、テナント作成時に設定するパラメータです。
FQDNごとに異なるS3プレフィックスを使用する今回の構成では、FQDNごとにディストリビューションテナントを作成し、Domainとパラメータ値の一致を確実に管理することが重要です。
一方、オリジンとしてS3バケットではなくEC2インスタンスなどのWebサーバを指定する場合はまた違った考慮が必要となります。
いずれにしてもマルチテナント・ディストリビューションはいろいろな工夫の余地のある面白い機能であることは間違いありません。
SSGやヘッドレスCMSなどと組み合わせても面白いかと思います。

