WordPressの記事を静的HTML化しS3+Cloudfrontで静的Webホスティング(+認証)


WordPressからボタンひとつで更新出来る
会員制の静的Webサイトを作る

こんにちは、やすです。

ブログを書くのは何年ぶりかな。。

WordPressはPHPで動的に動作する為、大量なアクセスを捌く為には色々な工夫が必要になります。

そのため、Wordpressの記事をS3+Cloudfrontで静的ホスティングは有効な手段の一つかと思います。

但し、この場合、静的HTMLとなるため、セッション認証のような認証機構を使う事が出来ません。
それを解決する為にCloudfrontの署名付きCookieを利用したサイトを構築しました。

全体の流れは下記のような感じですので、詳細はそれぞれ説明していきます。

  1. WordPressの記事を静的HTMLとして出力
  2. 出力した静的HTMLをS3へのアップロード
  3. S3に配置した静的HTMLをCloudfront経由で配信
  4. 認証機構をもたせる為にCloudfrontの署名付きCookieを利用

WordPressの静的HTML化(プラグイン利用)

WordPressの静的HTML化は、既存のプラグインを利用しました。

静的サイト生成プラグインはいくつかありますが、代表的なものは下記のプラグインだと思います。

それぞれ試してみましたが、StaticPress、Simply Staticは思った通りに動作しませんでした。

またプラグインの更新状況もStaticPressは3年以上前、Simply Staticも9ヶ月前という状況(2019年2月現在)の為、WP2Staticプラグインを利用する事にしました。

尚、WP2Staticプラグインは有料プランも用意されているようですが、今回は有料プランまでは不要だった為、有料プランは使用していません。

WP2Staticプラグインでは、出力先を同一サーバーのサブディレクトリ以外にも、FTP経由でファイルを送ったりNetflifyにアップしたりDropboxでバックするような事も出来ます。

S3へのアップロードは有料プラグインで利用可能です。

私の用途では、S3アップロード時に他の処理も行う必要があった為、同一サーバーのサブディレクトリに出力する方式を取っており、その後のS3へのアップロードは別途独自プラグインとして作成しています。

S3へのアップロード(自作プラグイン)

S3へのアップロードは上述の通り独自プラグインを作成しました。

アップロード処理はAWS SDK for PHPを利用します。

Composer ( https://getcomposer.org/ )を利用し下記コマンドでインストールします

$ composer require aws/aws-sdk-php

生成した静的HTMLはフォルダ毎、S3へアップロードしています。

プラグインの処理の概要は下記のようになります。

AWS設定

S3設定

CloudFront のオリジンに、 S3 バケットそのものを指定する構成としています。

CloudFront のオリジンには、S3 バケットの Static website hosting のエンドポイントを指定する構成にも出来ますが、今回の要件としてはS3バケットをパブリックアクセス可能にしない必要がありますので、前者の構成としています。

S3バケットのアクセス権限は「オブジェクトは公開可能」つまり非公開だがアクセス許可があればアクセス可能という状態にします。

S3バケット内には後述する署名付きCookieを使う領域と使わない領域を分ける為に

/public : 一般公開

/restricted : 認証有り領域

としています。

そして、Wordressから出力した静的HTMLは /restricted 内にファイルをアップロードしています。

Cloudfront設定

Cloudfrontのオリジンに作成したS3バケットに指定します。

署名付きCookie

Cloudfrontの認証機能では、CloudFront 署名付き URL と署名付き Cookieが利用出来ます。

署名した文字列をURLに指定するかCookieに指定するかの違いになります。

署名付きURLではURL単位での署名になるため、複数の制限されたファイルへのアクセスを提供する場合は、署名付きCookieを利用する事なります。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-choosing-signed-urls-cookies.html

署名付きCookieについて詳しくは下記AWSのサイトに記載があります。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html

署名付きCookie(署名付きURL)を使用する為には、CloudfrontのBehaviorの設定にて下記の設定をします。

Restrict Viewer Access(Use Signed URLs orSigned Cookies):Yes
Trusted Signers:Self

※自分以外のアカウントを信頼するユーザーとする場合は、Trusted Signers:Specify Accounts とし別途アカウントを指定する必要があります。

この時点で、S3のURLへ直接アクセスすると下記のレスポンスとなります。
(一般公開していない為、403エラーとなります)

https://s3-ap-northeast-1.amazonaws.com/XXXXX/restricted/restricted.html

Status Code:403 Forbidden

<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>——</RequestId>
<HostId>
——
</HostId>
</Error>

またCloudfrontのURLへアクセスを行うと下記の応答となります。
(署名付きCookieか署名付きURLではない為、403エラーとなります)

https://XXXXX.cloudfront.net/restricted/restricted.html

Status Code:403 Forbidden

<Error>
<Code>MissingKey</Code>
<Message>
Missing Key-Pair-Id query parameter or cookie value
</Message>
</Error>

署名付きCookieを利用する為には、CloudfrontキーペアIDが必要になりますので事前にキーペアIDを発行しておく必要があります。

CloudfrontキーペアIDは、下記手順に従い作成してください。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs

キーペアIDが作成されると、CloudFront キーペアのプライベートキーをダウンロードすることが出来ます。

署名付きCookieの発行

下記のように、AWS SDK for PHPを利用し、CloudfrontキーペアのプライベートキーファイルとCloudfrontキーペアIDを使ってCookieを発行することが出来ます。

正常に署名付きCookieが取得出来ると、$signedCookieCannedPolicyの中は、キー値が、CloudFront-Policy、CloudFront-Signature、CloudFront-Key-Pair-Idの配列になっています。

署名付きCookieを使ってページを表示

HTMLヘッダーのCookieのCloudFront-Policy、CloudFront-Signature、CloudFront-Key-Pair-Idに取得したCookieの情報を使用し先程のページにアクセスするとエラーがなくHTMLを表示することが出来ます。

例としてcurlコマンドでアクセスした例です。

$ curl -H 'Cookie: CloudFront-Policy=XXXXXX; CloudFront-Signature=XXXXXX; CloudFront-Key-Pair-Id=XXXXX’ https://XXXXX.cloudfront.net/restricted/restricted.html
<html>
  <body>
    restricted
  </body>
</html>

HTMLが表示されればOKです!

署名付きCookieが不要な領域を用意する

これまでで署名付きCookieがなければ表示できない状態が作れました。

しかし同じドメイン内(同一S3バケット内)で署名付きCookieが必要ない領域を作りたいという場合も出てくるかと思います。

そのような場合は、CloudfrontのBehaviorsを追加します。

例として /public 以下は署名付きCookie無しでアクセス出来るようにします。
Path Pattern /public/*
こちらは
Restrict Viewer Access No
とします。

このようにすることで
https://XXXXX.cloudfront.net/restricted/
配下のURLは非公開で署名付きCookieがあれば表示でき、
https://XXXXX.cloudfront.net/public/
配下のURLは署名付きCookie無しでも公開されるという状態を作ることが出来ました。

おしまい。


この記事を書いた人