デザインテンプレートの豊富さ、端末に依存しないUI、コンテンツ管理のしやすさなど、ホームページを作る際にWordpress は必須と思いますが、プラグインを含めた脆弱性対策が難しく、そのままインターネットに晒すのはちょっと勇気が要ります。そこで私は、Wordpress で作ったコンテンツを静的コンテンツに変換して、AWS の Cloudfront で公開する方法を取っています。使えなくなってしまう Wordpress の便利な機能も多いですが、それが許容できる方にはお薦めします。今回は、2024年3月に会社のホームページを作った際の作業記録を残しておきます。
Wordpress サイトを AWS Lightsail で動かす
Wordpress を動かすサイトは、インターネットで公開するつもりがないので、ローカルPC上でも構いません。私の場合、どの端末からでもアクセスできるようにしたかったので、安価で管理が楽なものをと探して、AWS Lightsail を使うことにしました。
Lightsail には、最初からWordpressがインストールされたインスタンスイメージがあるので、それを選択すれば、数分でWordpress を使い始められます。Wordpress 用のインスタンスのスペックは 1GB RAM / 2vCPU / 40GB SSD を勧められるのでそれを選びます。IPv4アドレスはStatic で1個付けると、自分のドメインのホスト名と紐付けて、ブラウザからその FQDN でアクセスできるようになります。料金は1ヶ月固定で 7.00 USD(約1,100円)。
こうして作ったWordpress 環境でホームページを作っていきます。ここではあくまでコンテンツを作るだけなので、使い終わったらインスタンスは停めておきます。動かしっぱなしでも料金は変わりませんが、脆弱性対応のアップデートなどを頻繁にやらず、コンテンツを更新する際に立ち上げてついでにアップデートするという運用で、日頃は Wordpress の脆弱性を気にせずに済みます。
Wordpress で静的コンテンツを作る
静的コンテンツを作るためのプラグインはいろいろあるようですが、私は Simply Static を使っています。このプラグインを使って「Generate Static Files」ボタンを押すと、Lightsail で動いているLinux マシン上に静的コンテンツが生成されます。Lightsail のWordpress をデフォルトのまま使っている場合、/opt/bitnami/wordpress/public_static/ に配置されます。
静的コンテンツを S3 にコピーする
生成した静的コンテンツを Cloudfront で公開するために、AWS S3 にコピーします。S3へのローカルファイルのコピーにはAWS CLIのインストールや環境設定が必要です。その方法の詳細は、以前書いた「S3バケットのコピーを作る」をご覧ください。
静的コンテンツを S3にコピーする際には、Lightsail で動いているLinux マシンのコンソール(Lightsail の管理画面からアクセス可能)にて、次のようなコマンドを実行します。
aws s3 cp /opt/bitnami/wordpress/public_static s3://copy.foo.bar --recursive
Cloudfront で S3 コンテンツを公開する
公開したいコンテンツが S3にあるので、あとは AWS Cloudflont で設定して公開するだけです。Cloudfront での設定方法は、詳しいチュートリアルをAWS が提供してくれているので、こちらを参考にしましょう。
このチュートリアルに則ってコンテンツを公開すると、https://foo.bar/index.html のように実在するファイルをアクセスすることができますが、https://foo.bar/ のようにディレクトリアクセス(ルートアクセス)すると、Access Denied というメッセージが表示されてしまいます。
この現象を解消するために、CloudFront の関数(Function)を作り、それをCloudfront のディストリビューションに適用する、という一手間が必要です。
Cloudfront 上で次の関数を作り、それらをディストリビューションに関連付けすることで、ルートアクセスでもコンテンツにアクセスできるようになります。
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;
}