1. はじめに
前回の記事では、AWS CDKを使用して、Nuxt SPAの静的ファイルを配置するためのS3バケットを構築しました。
今回はその続きとして、Amazon CloudFrontを使用し、S3に配置したNuxt SPAをWebブラウザから配信するための構成を作成します。
前回構築したS3バケットは、パブリックアクセスをブロックしているため、インターネットから直接アクセスできない設定になっています。
そこで、CloudFrontを経由してS3内のコンテンツを配信し、S3バケットを非公開にしたままWebアプリケーションを公開できる構成を目指します。
この記事では、CloudFrontの基本的な仕組みを整理したうえで、S3との連携方法やOrigin Access Control(OAC)によるアクセス制御、AWS CDKを使用した構築方法について解説します。
2. Amazon CloudFrontとは
Amazon CloudFrontは、AWSが提供するContent Delivery Network(CDN)サービスです。
CDNとは、Webサイトの画像やHTML、JavaScript、CSSなどのコンテンツを、利用者に近い拠点から効率よく配信する仕組みです。
通常、Webサイトにアクセスすると、ブラウザからWebサーバーへリクエストが送信されます。
しかし、Webサーバーと利用者の物理的な距離が離れている場合、通信に時間がかかることがあります。
CloudFrontを利用することで、世界各地に配置されたエッジロケーションを活用し、コンテンツを効率よく配信できます。
2.1 CDNの仕組み
CDNを使用しない場合、利用者はコンテンツが保存されているサーバーへ直接アクセスします。
一方、CDNを使用する場合は、利用者からのリクエストをCloudFrontが受け付けます。
CloudFrontのキャッシュにコンテンツが存在する場合は、そのキャッシュからレスポンスを返します。
キャッシュに存在しない場合は、配信元となるサーバーからコンテンツを取得し、利用者に返します。
この仕組みにより、配信元へのアクセス回数を減らしたり、コンテンツの配信速度を改善したりできます。
2.2 エッジロケーションとは
エッジロケーションとは、CloudFrontがコンテンツを配信するために使用する、世界各地に配置された拠点です。
CloudFrontは利用者からのリクエストを適切なエッジロケーションへルーティングし、可能な場合はキャッシュされたコンテンツを返します。
例えば、日本から海外リージョンのサーバーにアクセスする場合でも、近くのエッジロケーションにキャッシュがあれば、配信元まで毎回アクセスする必要がありません。
なお、エッジロケーションとAWSのAZは異なるものです。
- AZ: EC2やRDSなどのAWSリソースを配置する、リージョン内の独立したデータセンター群。主に、複数のAZにリソースを分散配置することで、障害時の影響を抑え、システムの冗長化や可用性の向上を図るために利用されます。
- エッジロケーション: CloudFrontなどがコンテンツを利用者の近くから配信するための拠点。主に、コンテンツをキャッシュして利用者に近い場所から配信することで、通信の遅延を減らし、Webサイトの表示速度を向上させるために利用されます。
CloudFrontでは、これらのエッジロケーションを利用してコンテンツ配信を最適化しています。
2.3 CloudFrontのキャッシュ
CloudFrontの大きな特徴の一つがキャッシュ機能です。
キャッシュとは、一度取得したコンテンツを一時的に保存し、次回以降のリクエストで再利用する仕組みです。
例えば、複数の利用者が同じ画像へアクセスした場合、CloudFrontにキャッシュがあれば、毎回S3から画像を取得せずにレスポンスを返せます。
これにより、以下のようなメリットがあります。
- コンテンツの配信速度を改善できる
- S3などのオリジンへのアクセスを削減できる
- 大量のアクセスにも対応しやすくなる
ただし、コンテンツを更新しても古いキャッシュが残る場合があります。
そのため、Webアプリケーションの更新時には、キャッシュの有効期限や無効化についても考慮する必要があります。
3. S3とCloudFrontを組み合わせる理由
前回構築したS3バケットは、Nuxt SPAのHTMLやJavaScript、CSSなどの静的ファイルを保存する場所として利用します。
S3自体にも静的Webサイトホスティング機能がありますが、今回はその機能を使わず、CloudFrontを経由してコンテンツを配信する構成にします。
主な理由は、以下のとおりです。
| 項目 | 説明 |
|---|---|
| セキュリティ | S3バケットを非公開にしたままコンテンツを配信できる |
| HTTPS通信 | CloudFront経由でHTTPSを利用できる |
| 配信性能 | エッジロケーションとキャッシュによって効率的に配信できる |
| AWSとの連携 | 将来的に独自ドメインやAWS WAFなどとの連携も可能 |
今回の構成では、S3をコンテンツの保存先、CloudFrontをコンテンツの配信元として利用者から見える入口にすることで、それぞれの役割を分けています。
3.1 オリジンとは
CloudFrontでは、コンテンツの取得元を オリジン(Origin) と呼びます。
オリジンには、S3バケットやWebサーバーなどを指定できます。
今回の場合、前回構築したS3バケットがオリジンに該当します。
利用者からリクエストを受け付けたCloudFrontは、必要に応じてオリジンであるS3からファイルを取得し、利用者へ返します。
3.2 OACとは
今回の構成では、CloudFrontからS3へ安全にアクセスするためにOACを使用します。
OACは、CloudFrontがS3オリジンへアクセスする際の認証・アクセス制御を行う仕組みです。
通常、S3バケットを非公開にすると、インターネットから直接ファイルを取得することはできません。
しかし、OACを利用してCloudFrontに必要な権限を付与することで、S3バケットを公開せずにコンテンツを配信できます。
今回の構成では、S3のパブリックアクセスブロックを維持したまま、CloudFrontディストリビューションからのアクセスを許可します。
なお、OACは以前使用されていたOrigin Access Identity(OAI)に代わる、推奨されているアクセス制御方式です。
4. 今回のAWS構成
今回構築するAWS構成は以下のとおりです。
ユーザーはCloudFrontのドメイン名にアクセスします。
CloudFrontはリクエストを受け付け、キャッシュが存在する場合は、そのキャッシュからコンテンツを返します。
キャッシュが存在しない場合は、オリジンであるS3からファイルを取得し、ユーザーへ返します。
この際、OACを使用することで、S3バケットを非公開にしたままCloudFrontからのアクセスを許可できます。
なお、図中のOACはアクセス制御の仕組みを表しており、独立した中継サーバーではありません。
5. AWS CDKでCloudFrontを構築
ここからは、AWS CDKを使用してCloudFrontを定義します。
前回のS3編で作成した sesame-infra-stack.ts にCloudFrontの定義を追加します。
5.1 CloudFront関連のモジュールをインポート
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
CloudFrontを構築するために必要なモジュールをインポートします。
-
cloudfront:CloudFrontディストリビューションなどを定義する -
origins:CloudFrontがコンテンツを取得するオリジンを定義する
今回は、S3バケットをオリジンとして使用します。
5.2 CloudFrontディストリビューションを定義
// CloudFrontから非公開S3バケットを配信
const distribution = new cloudfront.Distribution(this, 'FrontendDistribution', {
defaultBehavior: {
origin: origins.S3BucketOrigin.withOriginAccessControl(frontendBucket),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
},
defaultRootObject: 'index.html',
});
このコードでは、CloudFrontディストリビューションを作成し、S3を配信元として設定しています。
各設定項目について解説します。
① defaultBehavior
defaultBehavior: {
// ...
},
CloudFrontがリクエストを受け付けた際の、デフォルトの動作を定義します。
どのオリジンからコンテンツを取得するか、HTTP・HTTPS通信をどのように扱うかなどを設定できます。
② origin
origin: origins.S3BucketOrigin.withOriginAccessControl(frontendBucket),
CloudFrontのオリジンとして、前回作成したS3バケットを指定します。
withOriginAccessControl() を使用することで、S3オリジンに対するOACを設定します。
これにより、CloudFrontからS3へのアクセスを制御するための設定や、必要なバケットポリシーの許可をCDKで管理できます。
③ viewerProtocolPolicy
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
CloudFrontにアクセスするユーザーの通信プロトコルを制御します。
REDIRECT_TO_HTTPS を指定すると、HTTPでアクセスしたユーザーをHTTPSへリダイレクトします。
これにより、ブラウザとCloudFront間の通信でHTTPSを利用する構成になります。
④ defaultRootObject
defaultRootObject: 'index.html',
CloudFrontのルートURLにアクセスした際に返すデフォルトのファイルを指定します。
例えば、以下のURLへアクセスした場合です。
https://example.cloudfront.net/
CloudFrontはルートへのリクエストに対して、index.html を取得するように処理します。
Nuxt SPAでは、通常 index.html がアプリケーションの入口となるため、このファイルを指定しています。
ただし、この設定だけでは /users などのSPA内部ルートへの直接アクセスに対応できません。
SPAのルーティング対応については、後続の構築でCloudFront Functionを追加して対応する予定です。
5.3 CloudFrontのドメイン名を出力
続いて、CDKによるデプロイ後にCloudFrontのドメイン名を確認できるよう、CloudFormationの出力を設定します。
new cdk.CfnOutput(this, 'CloudFrontDomainName', {
value: distribution.distributionDomainName,
description: 'CloudFront domain name for Nuxt frontend',
});
distribution.distributionDomainName には、CloudFrontディストリビューションに割り当てられるドメイン名が入ります。
例えば、次のような形式です。
dxxxxxxxxxxxxx.cloudfront.net
このドメイン名を使用して、作成したCloudFrontへアクセスできます。
今回の構成では、独自ドメインは設定せず、CloudFrontが発行するドメイン名を利用します。
6. CDKの差分確認とデプロイ
CloudFrontのコードを追加したら、AWS環境へ反映する前に、TypeScriptのエラーや既存リソースへの変更内容を確認します。
6.1 TypeScriptの確認
まず、以下のコマンドでTypeScriptの型チェックを実施します。
npx tsc --noEmit
--noEmit を指定することで、JavaScriptファイルを出力せずにTypeScriptのエラーの有無を確認できます。
6.2 CDKによる変更差分の確認
次に、以下のコマンドで現在のAWS環境とCDKコードの差分を確認します。
npx cdk diff SesameInfraStack --profile sesame-dev
cdk diff は、CDKコードから生成したCloudFormationテンプレートと、デプロイ済みのスタックを比較するためのコマンドです。
今回の変更差分では、以下のリソースが追加・変更される想定です。
| 変更 | AWSリソース | 内容 |
|---|---|---|
[+] |
CloudFront OriginAccessControl | OACの新規作成 |
[+] |
CloudFront Distribution | CloudFrontディストリビューションの新規作成 |
[~] |
S3 BucketPolicy | CloudFrontからのアクセス許可を追加 |
[+] |
CloudFormation Output | CloudFrontのドメイン名を出力 |
実際の差分確認では、以下のような結果を確認できました。
① CloudFrontとOACの追加、S3バケットポリシーの変更
[+] はリソースの追加、[~] は既存リソースの変更を表しています。
今回は、CloudFrontディストリビューションとOACが新規作成され、既存のS3バケットポリシーにはCloudFrontからの s3:GetObject を許可する設定が追加されます。
また、アクセス許可には AWS:SourceArn の条件が指定されており、対象のCloudFrontディストリビューションからのアクセスに限定する構成となっています。
Outputsには CloudFrontDomainName が追加されています。
これは、CloudFrontの構築後に払い出されるドメイン名をCloudFormationの出力として確認するための設定です。
これらの差分から、CloudFrontの追加に必要なリソースとS3バケットポリシーの変更内容を確認できます。
また、既存のVPCやS3バケット自体に意図しない削除・置換が発生しないことも確認します。
6.3 CloudFrontのデプロイ
変更差分に問題がないことを確認したら、以下のコマンドでAWS環境へデプロイします。
npx cdk deploy SesameInfraStack --profile sesame-dev
cdk deploy は、CDKコードで定義したAWSリソースをCloudFormationを通じて作成・更新するコマンドです。
今回の場合は、既存のS3バケットをオリジンとするCloudFrontディストリビューションなどを追加します。
なお、CloudFrontはグローバルにコンテンツを配信するサービスのため、ディストリビューションの作成や設定変更が反映されるまで時間がかかる場合があります。
7. 動作確認
CloudFrontのデプロイが完了したら、Nuxt SPAの静的ファイルをS3バケットへアップロードし、CloudFront経由でWebページを取得できることを確認します。
今回は、以下の流れで動作確認を行います。
- Nuxt SPAの静的ファイルを生成
- 生成したファイルをS3へアップロード
- CloudFrontのドメイン名を取得
- CloudFront経由でHTMLを取得できることを確認
- ブラウザでWebページの表示を確認
7.1 Nuxt SPAの静的ファイルを生成
まず、Nuxtプロジェクトのディレクトリへ移動します。
cd C:\work\sesame-admin\frontend
続いて、以下のコマンドで静的ファイルを生成します。
npm run generate
npm run generate を実行すると、Nuxt SPAをS3などの静的ホスティング環境に配置できる形式で生成できます。
生成されたファイルは .output/public ディレクトリに配置されます。
以下のコマンドで、生成されたファイルを確認します。
Get-ChildItem .output/public
出力例
Mode Name
---- ----
d---- _nuxt
d---- companies
d---- invoices
d---- signup
-a--- 200.html
-a--- 404.html
-a--- favicon.ico
-a--- index.html
-a--- robots.txt
index.html や _nuxt ディレクトリなどが生成されていることを確認します。
index.html はSPAの入口となるHTMLファイルで、_nuxt ディレクトリにはJavaScriptやCSSなどのアセットが含まれています。
※実際に生成されるファイルはNuxtの設定やアプリケーションの構成によって異なります。
7.2 S3バケットへファイルをアップロード
続いて、生成した静的ファイルをS3バケットへアップロードします。
aws s3 sync .output/public `
s3://sesameinfrastack-frontendbucketefe2e19c-ccratagxz4ai `
--profile sesame-dev `
--region ap-northeast-1
aws s3 sync は、ローカルディレクトリとS3バケットの内容を同期するコマンドです。
今回は .output/public 内のファイルをS3バケットのルートへ配置します。
アップロード完了後、以下のコマンドでS3内のファイルを確認します。
aws s3 ls `
s3://sesameinfrastack-frontendbucketefe2e19c-ccratagxz4ai/ `
--recursive `
--profile sesame-dev
index.html や _nuxt/ 配下のファイルが表示されれば、S3へのアップロードが確認できます。
なお、今回のS3バケットは非公開のため、ファイルをアップロードしただけではインターネットから直接参照できません。
7.3 CloudFrontのドメイン名を確認
続いて、CloudFrontのドメイン名を確認します。
aws cloudformation describe-stacks `
--stack-name SesameInfraStack `
--query "Stacks[0].Outputs[?OutputKey=='CloudFrontDomainName'].OutputValue" `
--output text `
--region ap-northeast-1 `
--profile sesame-dev
このコマンドでは、CDKコードで定義した CloudFrontDomainName の出力値を取得します。
出力例(サンプル)
d123456789example.cloudfront.net
このドメイン名が、CloudFront経由でWebアプリケーションへアクセスする際のURLになります。
7.4 CloudFront経由でHTMLを取得
取得したドメイン名を使用し、CloudFrontからHTMLファイルを取得できることを確認します。
PowerShellから以下のコマンドを実行します。
curl.exe -I https://d123456789example.cloudfront.net/
※URLは7.3で取得したドメイン名に置き換えます。
正常時のレスポンス例(サンプル)
HTTP/2 200
content-type: text/html
x-cache: Miss from cloudfront
HTTPステータスコード 200 が返されれば、CloudFrontがリクエストに対して正常にレスポンスを返していることを確認できます。
さらに、以下のコマンドでHTMLの内容も確認します。
curl.exe https://d123456789example.cloudfront.net/
正常に取得できた場合は、<!DOCTYPE html> などを含むHTMLが表示されます。
これにより、CloudFront経由でS3に配置した静的ファイルを取得できることを確認できます。
7.5 ブラウザで表示確認
最後に、WebブラウザからCloudFrontのURLへアクセスします。
https://d123456789example.cloudfront.net/
Nuxt SPAの画面が正常に表示されれば、以下の一連の流れを確認できます。
Webブラウザ → CloudFront → OACによるアクセス制御 → S3 → HTML・JavaScript・CSSの配信
これにより、S3バケットを非公開にしたまま、CloudFront経由でWebアプリケーションを配信できる構成になっていることを確認できます。
なお、今回の構成ではSPAの内部ルートへの直接アクセスに対する設定はまだ追加していません。
そのため、/users などのURLへ直接アクセスした場合に発生する問題については、別途CloudFront Functionを使用したルーティング設定で対応する必要があります。
8. まとめ
今回は、Amazon CloudFrontの基本的な仕組みと、S3をオリジンとして利用する構成について学習しました。
次回は、AWS Lambdaを使用して、Webアプリケーションのバックエンド処理を実行するための環境を構築します。



