CloudFrontマルチテナント・ディストリビューションとS3でLPをホストする

年三日坊主の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などと組み合わせても面白いかと思います。