WordPressのためのCloudFront最適化ガイド: SimplyStaticとS3の活用術

WordPressを使ってブログやウェブサイトを運営している方々にとって、ホスティングのコストやセキュリティは常に大きな関心事です。特に、アクセス数が増えるにつれて、サーバーの負荷やコストが高騰することは避けたいところ。そこで、今回は「SimplyStatic」というプラグインを活用して、WordPressのサイトを静的サイトとしてAWSのS3にホスティングし、さらにCloudFrontを使ってアクセスを高速化・分散させる方法をご紹介します。
この方法の最大の魅力は「安価」で「セキュリティ」が高い点です。従来のホスティングサービスに比べて、S3は非常に低コストで大量のデータを保存できるため、長期的に見ても経済的です。また、静的サイトとして公開することで、WordPress特有のセキュリティリスクを大幅に軽減することができます。CloudFrontを組み合わせることで、世界中からのアクセスもスムーズにさばくことができるので、ユーザーエクスペリエンスも向上します。
興味を持たれた方は、このブログを最後までお読みいただき、新しいホスティングの方法を試してみてください。安価でセキュアなウェブサイト運営の一助となれば幸いです。とはいえ、静的なHTMLがベースとなりますので、お問い合わせフォームや記事へのコメントは難しいようで、動的な処理への機能制限がかかってしまうことはありますのでご注意ください。
今回の構成は、以下のようになっており、
WordPress(EC2):http://XX.XXX.XXX.XXX (EC2のIPアドレス)
CloudFront+S3:https://deeplog.work
最終的にWordPressで出力した記事をS3にAWS CLI(コマンドラインインターフェイス)でアップロードして表示させます。
WordPressは執筆と記事出力専用となり、表示は「CloudFront+S3」側で行います。ですので、コスト削減の観点から、執筆時以外は、EC2は停止させておくことができます。
AWS S3にデータ保存用バケットを新規作成
S3の[バケットを作成]からデータ保存用バケットを新規作成します。(下記は設定例です)
一般的な設定
- バケット名は任意で命名規則に従い入力します。
- AWSリージョンは東京を選びました。

オブジェクト所有者
- オブジェクト所有者:デフォルトのまま、ACL無効(推奨)を選択する。

このバケットのブロックパブリックアクセス設定
- このバケットのブロックパブリックアクセス設定:すべてオフ(チェックをはずす)にする。S3は直接公開するわけではなく、後述するCloudFrontとの組み合わせで公開するので、オフにして問題なし。

バケットのバージョニング
初期値のままです。
- バケットのバージョニング:無効にする

デフォルトの暗号化
初期値のままです。
- 暗号化タイプ:SSE-S3
- バケットキー:有効にする

ACMでSSL証明書の新規発行
今回前提として、ドメインはお名前.comで管理しているドメインを使用します。そのため、ネームサーバーをRoute53を使用し、AWSに切り替え、その後にACMでSSL証明書を発行する手順を記載します。
Route53でホストゾーンの作成
お名前.comで登録しているドメインで、AWSのRoute53というサービスでホストゾーンの作成を行います。
ホストゾーン設定
- ドメイン名:該当のドメイン名「(例)deeplog.work」を入力します。
- タイプ:パブリックホストゾーンを選択します。
その他はデフォルトのままで[ホストゾーンの作成]ボタンを押します。

ホストゾーンの登録をすることでAWSのネームサーバーが割り当てられます。

このネームサーバーをお名前.com側に登録します。
お名前.comでのネームサーバー切り替え
お名前.com側にアクセスし、お名前.comのネームサーバーを先ほどAWSのRoute53で発行したネームサーバーに切り替えます。[ネームサーバーの変更]をクリックします。

「他のネームサーバーを利用」のタブをクリックします。該当ドメインのネームサーバーを、AWSのネームサーバーを入力し[確認画面へ進む]をクリックします。

その次の画面では、設定内容に間違いが無ければ、[設定する]ボタンをクリックします。お名前.com側の設定は以上で完了です。
「環境により反映完了まで24時間から72時間程度かかる場合がございます。」と画面に注意書きがあるように、ネームサーバーがAWSのものに切り替わるまで、タイムラグがある場合もあります。通常、何も問題無ければ、10分以内で切り替えが完了している印象です。
AWS CertificateManager (ACM)でSSL証明書の発行
お名前.com側のネームサーバーの切り替えの完了後、リージョンは、「us-east-1:米国東部(バージニア北部)」を選択してACMで証明書をリクエストします。※後ほど、CloudFrontで証明書の選択を行うとき、us-east-1リージョンで発行したSSL証明書でないと使用できないためです。

[リクエスト]をクリックして、証明書をリクエストしましょう。
証明書タイプ
証明書タイプ:パブリック証明書をリクエスト
を選んで[次へ]をクリックします。「パブリック証明書をリクエスト」の画面に進みます。

ドメイン名
- 完全修飾ドメイン名:該当のドメイン名「(例)deeplog.work」を入力します。

検証方法
- 「DNS検証」を選択します。

キーアルゴリズム
- 「RSA 2048」を選択します。

[リクエスト]をクリックして、証明書をリクエスト完了しましょう。
その後は、「保留中の検証」という状態なので、証明書の一覧から、ドメイン名「(例)deeplog.work」の詳細画面を表示し、[Route53でレコード作成]ボタンをクリックします。

2分前後で証明書が発行済みとなりました。

以上でSSL証明書の発行が完了です。
AWS S3アクセス用のIAMユーザーを新規作成
IAMでS3にデータをアップロードするためのユーザーを新規作成します。WordPressで作成した記事を、後ほどローカルのPCからAWS CLIを使用してS3にアップロードする専用のユーザーです。
IAMのサービスにアクセスします。
サイドメニューの「アクセス管理>ユーザー」を選択します。
[ユーザーの作成]をクリックし、IAMユーザーの作成画面に遷移します。
ユーザーの詳細
- ユーザー名:該当のドメイン名「(例)deeplog.work」を入力します。
- AWSマネジメントコンソールへのユーザーアクセスを提供する:ON
- ユーザーにコンソールアクセスを提供していますか?:IAM ユーザーを作成します
- コンソールパスワード:自動生成されたパスワード
- ユーザーは次回のサインイン時に新しいパスワードを作成する必要があります:ON

上記の内容で作成を進めます。
今回作成したバケットに限定したポリシー
AmazonS3FullAccessを使用することはせず、今回新規作成したバケットに限定したアクセス権をもつポリシー「S3-WordpressBucketAccess」を新規で作成しアタッチします。
[ポリシーの作成]をクリックします。

アクセス許可を指定
「ポリシーエディタ」を編集します。

JSONのテキストは下記です。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ObjectLevel",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::wordpress-bucket/*"
},
{
"Sid": "BucketLevel",
"Effect": "Allow",
"Action": [
"s3:GetBucketPublicAccessBlock",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:aws:s3:::wordpress-bucket"
}
]
}
[次へ]をクリックし、確認画面に遷移し、内容に問題がなければ作成を完了させます。
ポリシーを直接アタッチする
IAMユーザーの作成画面に戻ります。そして新規作成したポリシーをアタッチします。「カスタマー管理」で絞り込むと、新規作成したポリシー「S3-WordpressBucketAccess」が表示されます。

ポリシーを選択して、[次へ]をクリックし、あとはデフォルトのまま作成を進めましょう。
ユーザーが正常に作成されました。
許可ポリシーは「IAMUserChangePassword」「S3-WordpressBucketAccess」の2つが付与されていると思います。
上記の権限だと、作業中エラーになったので、作業を進めるために「AmazonS3FullAccess」を付与しました。

アクセスキーをダウンロード
AWSのコマンドライン(CLI)で実行するために、アクセスキーを作成します。
[アクセスキーを作成]をクリックします。アクセスキー作成完了後、[.csvファイルをダウンロード]をクリックし、作成したアクセスキーはダウンロードしておきましょう。

ダウンロードのチャンスはアクセスキー作成直後のみですので気をつけてください。この情報は、後ほどS3にファイルをアップロードする時に使用します。
AWS CloudFrontの新規作成とS3の設定
AWS CloudFrontの作成と設定を進めましょう。
CloudFrontでディストリビューションを作成
CloudFrontでディストリビューションを作成します。S3にファイルを配置し、HTTPSでアクセスさせるために必要なサービスの1つであるCloudFrontです。
[ディストリビューションを作成]をクリックします。

オリジン
- オリジンドメイン︙Amazon S3で作成済のバケットを選択
- オリジンパス – オプション︙未入力
- 名前:選んだS3バケットの名前が表示されるのでそのまま
- オリジンアクセス
- [Origin access control settings(recommended)]を選択します。

[コントロール設定を作成]をボタンをクリックし、ダイアログにて設定します。
- 名前︙任意の一意の文字列を入力します。
- 説明:記載なし
- 署名リクエスト:「署名リクエスト(推奨)」を選択します。
- オリジンタイプ:「S3」を選択します。
[作成]ボタンをクリックします。

設定された項目でオリジンアクセスが設定されていればOKです。
オリジン
- カスタムヘッダーを追加 – オプション︙(デフォルト)追加なし
- オリジンシールドを有効にする︙(デフォルト)いいえ
その他の項目はデフォルトのままで設定し、現時点(2023年11月時点)では、SSL証明書の部分は発行したSSL証明書で設定する。※米国東部(バージニア北部)で証明書の発行が必要です。
設定
- カスタム SSL 証明書 :発行済みのSSL証明書(バージニア北部)を選択します。
※CloudFrontのディストリビューション作成後にはS3のバケットポリシーの設定が必要です。

以上の項目を設定し、CloudFrontのディストリビューションの作成を完了させます。
S3バケットポリシーを更新する必要があります
ディストリビューション作成後の上部ナビゲーションからS3のバケットポリシー変更画面に遷移します。
内容は[ポリシーをコピー]ボタンをクリックしてコピーしておき、コピーした内容で遷移先の画面にてバケットポリシーを編集します。
ポリシー

[変更の保存]ボタンをクリックして保存します。「バケットポリシーが正常に編集されました。」という表示が出て完了です。
独自ドメインでアクセスさせる設定
独自ドメインでアクセスさせるには、CloudFrontとRoute53で設定が必要です。
a.CloudFrontで代替ドメインの設定をします。
CloudFrontで先ほど作成したディストリビューションの「一般」項目の代替ドメインの追加を行います。
設定
- 料金クラス:「北米、欧州、アジア、中東、アフリカを使用」を選択します。
- 代替ドメイン:独自ドメインを割り当てましょう。「(例)deeplog.work」を入力します。

b.Route53でAレコードを作成します。
独自ドメインでアクセスするために、Route53でAレコードを作成します。
- レコードタイプ:A
- エイリアス:ON
- トラフィックのルーティング先:
CloudFrontディストリビューションへのエイリアス - CloudFrontディストリビューション名
- トラフィックのルーティング先:
- ルーティングポリシー:シンプルルーティング
[レコードを生成]ボタンを押し、Aレコードを作成します。

c.独自ドメインにアクセスします。
設定が完了すると、この時点でドメインにアクセスできるようになります。独自ドメインの紐づけは完了している状態ですが、まだファイルを設置していないのでErrorにはなります。
S3側のAccessDeniedエラーが表示されるはずです。
動作確認のためにS3にファイル設置
動作確認のために下記内容のファイルをindex.htmlという名前でS3にアップロードします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
</head>
<body>
<h1>Welcome to My Website</h1>
<p>This is a simple HTML5 template.</p>
</body>
</html>
上記の内容で設置し、再度URLにアクセスしてみるが、変わらず、エラーとなります。。。
https://ドメイン/index.htmlでアクセスすると表示されます(´・ω・`)
下層ページでもスラッシュ終わり/
な URL は、きちんと、そのディレクトリの中にある index.html を見に行くということができません。
デフォルト状態ですと、URLでindex.htmlは省略せずに最後まで入力するのが正解とのことです。
a.CloudFront Functionsの設定(関数の作成)を行います。
ドメインでアクセスしたときにindex.htmlの内容が表示されるように設定してみましょう。公式サイトの情報を参考に設定します。
詳細
- 名前:関数名を入力します。
[関数を作成]をクリックします。

関数作成後、関数コードを保存します。
関数コード
内容は参考サイトそのままです。

コードはこちらです。
function handler(event) {
var request = event.request;
var uri = request.uri;
// Check whether the URI is missing a file name.
if (uri.endsWith('/')) {
request.uri += 'index.html';
}
// Check whether the URI is missing a file extension.
else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}
b.関連付けを行います。
作成した関数とCloudFrontを紐づけます。[関数を発行]ボタンをクリックして関数コードを使用できるようにします。

- ディストリビューション:作成済のCloudFrontを選択します。
[関連付けを追加]ボタンをクリックして完了です。

SimplyStaicのインストールと設定・出力
ここからはWordPress側の設定となります。
WordPressのプラグイン「SimplyStaic」をインストールします。このプラグインはWordPress全体を静的HTMLとして出力するためのプラグインです。[今すぐインストール]をクリックしましょう。

WordPressのサイドメニューから「SimplyStaic」を選択します。

URLの設定
静的サイトをエクスポートするときの、WordPressサイトのリンクは、
- 絶対URL
- 相対URL
- オフライン
のいずれか、から選択します。
今回は「絶対パス(Absolute URLs)」で設定します。
- REPLACING URLS:「Absolute URLs」を選択します。
- SCHEME:「https://」を選択します。
- HOST:ドメイン名「(例)deeplog.work」を入力します。

エクスポート先の設定
出力方法は各自の要件に応じて決めます。ローカルディレクトリにエクスポートするか、SimplyCDNかの3つから選択します。
- ZIPファイルでダウンロード
- ローカルディレクトリにエクスポート
- SimplyCDN
今回は「ZIPファイルでダウンロード」で進めます。
(※EC2を利用している場合) EC2のセキュリティグループ見直しをします。
私は、ここで上記のように設定したあとSimply Staticで「出力」しても、全然反応がありませんでした。原因は長いこと不明だったのですが、WordPressを今回は、EC2で動かしていて、EC2に設定していたセキュリティグループで、特定のIPアドレスからしかHTTPアクセスを許可していませんでした。
それが原因でしたので、セキュリティグループのインバウンドルールは、どこからでもHTTPアクセスできるようにしました。タイプ:HTTPソース、ソース:「0.0.0.0/0」を設定します。

WordPressでEC2を使用していてうまく出力できない場合、セキュリティグループも原因の1つとして見直してみると良いと思います。
Generate Static Files:ファイルの出力
いよいよ出力です。うまく出力できるかどうか、わからなかったのですが、[Generate Static Files] ボタンをクリックしてから、すぐに出力できました。私の場合は、記事は5つしかありませんでしたので、所要時間は、12秒でした。Activity Logに表示されている「ダウンロードするにはこちらを」リンクを押すとファイルが無事ダウンロードできました。
あとは、このダウンロードしたファイルをS3にアップロードします。

「ダウンロードするにはこちらを」リンクを押すとファイルが無事ダウンロードできました。あとは、このファイルをS3にアップロードします。
フォルダとファイルをS3へアップロード
最後の手順です。ダウンロードしたファイルをAWS CLIで該当のS3にアップロードします。
以下は、AWS CLIをMacにて実行する手順です。
あらかじめAWS CLIはインストールしておきます。(難しい場合は、S3の管理画面から直接アップロードしても良いと思います。その場合、1点だけフォルダをいちいち作成しないとダメでした)
AWS CLIのインストール確認
AWS CLIがインストールされているかどうかを確認します。
% aws --version
aws-cli/2.4.26 Python/3.8.8 Darwin/21.6.0 exe/x86_64 prompt/off
AWSのセットアップ
IAMユーザーにて作成したユーザーdeeplog_userのアクセスキーとシークレットキーでログインします。
% aws configure --profile=deeplog_user
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: ZZZZZZZZZZZZZZZZZZZZZZZZ
Default region name [None]: ap-northeast-1
Default output format [None]: json
環境変数も設定します。
% export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXXXXXXXX
% export AWS_SECRET_ACCESS_KEY=ZZZZZZZZZZZZZZZZZZZZZZZZ
試しに、S3のバケット一覧情報を出力してみます。
% aws s3 ls --profile=deeplog_user
S3のバケット一覧が取得できていれば、問題なしです。
S3にアップロード
いよいよS3にアップロードします。S3のバケット名を指定して入力します。
- SimplyStaticでダウンロードしたフォルダ名:simply-static-download-folder
- S3のアップロードするバケット名:wordpress-bucket
上記のような値だった場合
% aws s3 cp simply-static-download-folder s3://wordpress-bucket/ --recursive
私は、アップロードしても表示が変わらなかったので、CloudFront側のキャッシュを削除する必要がありました。ですので、この手順も最後に加えておきます。
おまけ) CloudFront側のキャッシュ削除
- オブジェクトパスを追加:/index.htmlを入力します。
CloudFrontにて、[キャッシュ削除を作成]します。

以上で完了です。お疲れさまでした。