CloudFront Functions で S3 静的サイトの URL を書き換えてみた
はじめに
テクニカルサポートの 片方 です。
Amazon S3 バケットを通常の S3 バケットオリジンとして Amazon CloudFront に設定する場合、CloudFront のデフォルトルートオブジェクトはルートパス / にのみ適用されます。
そのため、S3 バケットに about/index.html を配置していても、/about や /about/ へのアクセス時に、自動的に about/index.html を返すことはできません。
本ブログでは、CloudFront Functions を使用して、閲覧者からのリクエスト URI を Viewer request イベントで書き換えます。具体的には、/about および /about/ へのアクセスを CloudFront 内部で /about/index.html に書き換え、S3 バケット上の HTML ファイルを配信します。
/about
↓ CloudFront Functions で URI を書き換え
/about/index.html
この処理は、ブラウザへ HTTP リダイレクトを返すものではありません。CloudFront 内部で URI を書き換えるため、ブラウザのアドレスバーは /about のままであり、/about/index.html へ遷移しません。
CloudFront Function で request.uri を変更すると、閲覧者が要求するオブジェクトを変更できます。一方で、URI の変更だけでは、キャッシュビヘイビアやオリジンは切り替わりません。本記事では、デフォルトのキャッシュビヘイビアに設定した Amazon S3 オリジンへ、書き換え後の URI を送信します。
また、検証環境では S3 バケットをパブリック公開せず、Origin Access Control (OAC) を使用します。これにより、対象の CloudFront ディストリビューションからのみ S3 オリジンへアクセスできるようにします。
検証内容
本記事で実施する内容は、以下のとおりです。
- S3 バケットに index.html と about/index.html を配置する
- CloudFront ディストリビューションを作成し、S3 バケットをオリジンとして設定する
- OAC を設定し、CloudFront からのみ S3 オリジンへアクセスできるようにする
- CloudFront Function を作成し、/about と /about/ を /about/index.html に書き換える
- https://<CloudFront-domain-name>/about および https://<CloudFront-domain-name>/about/ にアクセスし、About ページが表示されることを確認する
構成(検証環境)
今回の検証環境は、Amazon S3 をオリジンとする CloudFront ディストリビューションです。
CloudFront の Viewer request イベントに CloudFront Function を関連付け、/about および /about/ へのリクエストを /about/index.html に書き換えます。
[ブラウザ]
|
| GET /about
v
[Amazon CloudFront]
|
| CloudFront Function(Viewer request)
| /about -> /about/index.html
v
[Amazon S3]
|
| GET /about/index.html
v
[about/index.html を返却]
S3 バケットには、以下のオブジェクトを配置します。
<バケット名>/
├── index.html
└── about/
└── index.html
CloudFront ディストリビューションのデフォルトルートオブジェクトには、index.html を設定します。これにより、ルートパス / へのアクセス時に、S3 バケット直下の index.html を配信できます。
一方、S3 バケット内にオブジェクトキー about は配置しません。そのため、/about へのリクエストだけでは about/index.html を取得できません。
CloudFront Function で URI を /about/index.html に書き換えることで、S3 バケットに配置した about/index.html を取得できるようにします。
S3 バケットではパブリックアクセスブロックを有効にし、OAC を使用して CloudFront からのアクセスだけを許可します。また、S3 Object Ownership には Bucket owner enforced を設定します。
CloudFront では、OAC の利用が推奨されています。OAC を設定する際は、署名動作として Sign requests(recommended) を使用します。
使用する AWS サービス
| サービス | 用途 |
|---|---|
| Amazon S3 | 静的 HTML ファイルを配置するオリジン |
| Amazon CloudFront | コンテンツの配信、および S3 オリジンへのアクセス制御 |
| CloudFront Functions | Viewer request イベントで URI を書き換える |
| Origin Access Control (OAC) | CloudFront から S3 へのアクセスを許可し、S3 バケットのパブリック公開を回避する |
実装してみた
本節では、S3 バケット、CloudFront ディストリビューション、CloudFront Function を順に作成します。
- S3 バケットを作成し、HTML ファイルをアップロードする
- CloudFront ディストリビューションを作成し、OAC を設定する
- デフォルトルートオブジェクトに index.html を設定する
- CloudFront Function を作成し、Viewer request イベントに関連付ける
- CloudFront 経由でページを表示できることを確認する
S3 バケットを作成する
まず、静的 HTML ファイルを配置する S3 バケットを作成します。
S3 コンソールで バケットを作成 を選択し、任意のバケット名を入力します。本ブログでは、以下のような名前を使用します。
cloudfront-functions-url-rewrite-handson-<任意の文字列>
以下の設定でバケットを作成します。
| 項目 | 設定値 |
|---|---|
| バケット名 | 任意のグローバルで一意な名前 |
| パブリックアクセスをすべてブロック | 有効 |
| Object Ownership | Bucket owner enforced |
| バケットのバージョニング | 任意 |
| デフォルトの暗号化 | 任意 |

バケットを作成したら、以下のファイルを配置します。
<バケット名>/
├── index.html
└── about/
└── index.html
今回は動作確認用として、以下の HTML ファイルを作成しました。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>CloudFront Functions Handson</title>
</head>
<body>
<h1>トップページ</h1>
<p>CloudFront Functions の URL 書き換えを検証しています。</p>
<p><a href="/about">About ページへ</a></p>
</body>
</html>
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>About</title>
</head>
<body>
<h1>About ページ</h1>
<p>このファイルは S3 の /about/index.html に配置しています。</p>
<p>CloudFront Functions により、/about でアクセスできるようにします。</p>
<p><a href="/">トップページへ戻る</a></p>
</body>
</html>
index.html はバケット直下へアップロードします。続けて about プレフィックスを作成し、その配下に index.html をアップロードします。

CloudFront ディストリビューションを作成する
続いて、S3 バケットをオリジンとする CloudFront ディストリビューションを作成します。
CloudFront コンソールで Create distribution を選択します。コンソールの項目名や画面構成は更新される可能性があるため、以下の設定内容を確認しながら進めてください。
| 項目 | 設定値 |
|---|---|
| Distribution name | 任意の名前 |
| Description | 任意 |
| Distribution type | Single website or app |
| Route 53 managed domain | 必要に応じて設定 |

続いて、オリジンに以下を設定します。
| 項目 | 設定値 |
|---|---|
| Origin type | Amazon S3 |
| S3 origin | 作成済みの S3 バケット |
| Origin path | 空欄 |
| Allow private S3 bucket access to CloudFront | 有効 |
| Origin access control | 新規作成、または既存の OAC を選択 |
| Signing behavior | Sign requests (recommended) |

Allow private S3 bucket access to CloudFront を有効にすると、CloudFront から S3 バケットへアクセスするための OAC と、必要な S3 バケットポリシーを設定できます。
OAC の署名動作には、Sign requests (recommended) を設定します。この設定では、CloudFront から Amazon S3 へのリクエストに署名され、CloudFront と S3 間の通信には HTTPS が使用されます。
本ブログでは、S3 オリジン向けの推奨設定を使用します。
| 項目 | 設定値 |
|---|---|
| Origin settings | Use recommended origin settings |
| Cache settings | Use recommended cache settings tailored to serving S3 content |

次のセクションでは、今回は AWS WAF を有効化せずに進めます。

デフォルトルートオブジェクトを設定する
CloudFront ディストリビューションを作成した後、対象のディストリビューションを開きます。
Settings の General タブで Edit を選択し、Default root object に index.html を設定します。
index.html

これにより、CloudFront のドメイン名のルートパスへアクセスした場合に、S3 バケット直下の index.html を配信できます。
https://<CloudFront のドメイン名>/
この時点で、CloudFront のドメイン名へアクセスし、トップページが表示されることを確認します。
なお、この設定はルートパス / のための設定です。/about および /about/ を about/index.html に書き換える処理は、次節の CloudFront Function で設定します。

一方で、まだ CloudFront Function を設定していないため、以下の URL では about/index.html を取得できません。
https://<CloudFront のドメイン名>/about
CloudFront Function を作成する
CloudFront コンソールのナビゲーションペインで Functions を選択し、Create function を選択します。
関数名には、以下を設定します。
rewrite-about-to-index

Runtime には、cloudfront-js-2.0 を設定します。
関数を作成したら、Build タブで以下のコードを設定します。
function handler(event) {
var request = event.request;
if (request.uri === '/about') {
request.uri = '/about/index.html';
}
return request;
}
この関数では、リクエスト URI が /about または /about/ と完全一致する場合に、URI を /about/index.html に変更します。
| リクエスト URI | 書き換え後の URI |
|---|---|
| /about | /about/index.html |
| /about/ | /about/index.html |
| / | / |
| /about/index.html | /about/index.html |
| /assets/app.js | /assets/app.js |

今回は動作を分かりやすくするため、書き換え対象を /about と /about/ に限定しました。
実運用では、複数のページを対象にする場合でも、JavaScript、CSS、画像、フォント、API エンドポイントなどを誤って書き換えないよう、条件を慎重に設計してください。
コードを入力したら、Save changes を選択します。
CloudFront Function をテストする
Test タブで、以下の値を指定して CloudFront Function をテストします。
| 項目 | 設定値 |
|---|---|
| イベントタイプ | Viewer request |
| ステージ | DEVELOPMENT |
| HTTP メソッド | GET |
| URL パス | /about |

テスト結果の出力で、uri が /about/index.html になっていることを確認します。
{
"request": {
"method": "GET",
"uri": "/about/index.html"
}
}
続けて、URL パスを /about/ に変更してテストします。同様に uri が /about/index.html となることを確認します。
CloudFront Function を発行して関連付ける
テスト結果で、/about および /about/ が /about/index.html に書き換えられることを確認できたら、CloudFront Function を発行します。
Publish タブを開き、Publish function を選択します。
CloudFront Function は、作成直後は DEVELOPMENT ステージです。CloudFront ディストリビューションに関連付けるには、CloudFront Function を LIVE ステージへ発行する必要があります。


Function を発行した後、[関連付けを追加] を選択します。以下の内容で CloudFront ディストリビューションに関連付けます。
| 項目 | 設定値 |
|---|---|
| Distribution | 作成した CloudFront ディストリビューション |
| Event type | Viewer request |
| Cache behavior | Default (*) |


Viewer request は、CloudFront が閲覧者からリクエストを受信したタイミングで CloudFront Function を実行するイベントタイプです。
今回のように URI の書き換え結果をキャッシュキーに反映させたい場合は、Viewer request を使用します。
関連付け後、CloudFront ディストリビューションのステータスが Deployed になるまで待機します。
デフォルトのキャッシュビヘイビアへの関連付けを確認できたら、設定は完了です。お疲れさまでした!
続けて、実際の配信結果を確認します。
確認してみた
CloudFront Function をディストリビューションに関連付けると、CloudFront ディストリビューションの設定がデプロイされます。
ディストリビューションのステータスが Deployed になったことを確認してから、動作確認を実施します。
トップページを確認する
まずは、CloudFront ディストリビューションのドメイン名にアクセスします。
https://<CloudFront のドメイン名>/
S3 バケット直下に配置した index.html が表示されました。

今回は CloudFront ディストリビューションの Default root object に index.html を設定しているため、ルートパス / へのアクセス時に index.html が配信されます。
about へのアクセスを確認する
続いて、以下の URL にアクセスします。
https://<CloudFront のドメイン名>/about

S3 バケットに配置した about/index.html の内容である About ページが表示されました。
ブラウザのアドレスバーは /about のままですが、CloudFront Function により、CloudFront が S3 オリジンへ送信する URI は /about/index.html に書き換えられています。
閲覧者からのリクエスト
/about
↓
CloudFront Function
/about/index.html
↓
S3 から about/index.html を取得
直接 URL と比較する
比較のため、以下の URL にもアクセスします。
https://<CloudFront のドメイン名>/about/index.html

こちらも同じ About ページが表示されました。
| アクセスしたパス | CloudFront Function による URI | 表示結果 | ブラウザの URL |
|---|---|---|---|
| / | / | index.html を表示 | / |
| /about | /about/index.html | about/index.html を表示 | /about |
| /about/ | /about/index.html | about/index.html を表示 | /about/ |
| /about/index.html | /about/index.html | about/index.html を表示 | /about/index.html |
以上より、/about および /about/ へのアクセス時に、CloudFront Function で /about/index.html へ内部的に書き換えられていることを確認できました。
成功です!
まとめ
CloudFront Functions を使用して、/about へのリクエストを /about/index.html へ書き換える構成を試しました。
CloudFront Function を Viewer request に関連付けることで、ブラウザの URL を変更せずに、S3 上の about/index.html を配信できました。
今回は /about のみを対象としましたが、複数のパスを扱う場合は、意図しない URI まで書き換えないように条件を設計する必要があります。特に、API のパスや JavaScript、CSS、画像などの静的アセットは、書き換え対象から除外することを検討してください。
本ブログが誰かの参考になれば幸いです。
参考資料
- CloudFront Functions event structure - Amazon CloudFront
- Restrict access to an AWS origin - Amazon CloudFront
- Restrict access to an Amazon S3 origin - Amazon CloudFront
- Associate functions with distributions - Amazon CloudFront
クラスメソッドオペレーションズ株式会社について
クラスメソッドグループのオペレーション企業です。
運用・保守開発・サポート・情シス・バックオフィスの専門チームが、IT・AIをフル活用した「しくみ」を通じて、お客様の業務代行から課題解決や高付加価値サービスまでを提供するエキスパート集団です。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、クラスメソッドオペレーションズ株式会社 コーポレートサイト をぜひご覧ください。※2026年1月 アノテーション㈱から社名変更しました







