※このブログは ヌーラボブログリレー2023 for Tech Advent Calendar 2023 の18日目の記事です。
こんにちは!サービス開発部SRE課のZakiです。
BacklogのWordPressウェブサイトの表示速度を速くしたので、その内容について共有します。
目次
動機
BacklogのWordPressウェブサイトの表示速度が遅かったことが全ての始まりです。体感でも遅いと感じていましたが、Pingdom で測定した表示速度は6.5秒でした。表示速度を速くすることで、訪問して頂ける皆さんのユーザー体験を良くし、CVの改善に貢献できるのではと考えました。
構成
BacklogのWordPressウェブサイトの構成は上記の通りです。
- クラウド: AWS
- CMS: WordPress
- CDN: CloudFront
- キャッシュによるコンテンツ配信をせず、HTTPからHTTPSへのリダイレクトのみ有効
- Web server: Nginx
- NginxのFastCGIキャッシュは設定済み
実施したこと
KINSTAさんの【2023年決定版】WordPressサイト高速化のヒントを元に、改善効果が高そうなタスクを優先的に実施しました。
まずはキャッシュ周りの最適化として、ブラウザキャッシュの設定とCloudFrontでのコンテンツ配信です。次にプラグインが表示速度に影響を与えている可能性を疑い、不要プラグインを削除しました。
まとめると次の通りです。
- ブラウザキャッシュの設定
- CloudFrontでのコンテンツ配信
- 不要プラグインの削除
中身を見ていく前に、キャッシュについて説明します。すでにご存知の方は、ブラウザキャッシュの設定にお進みください。
キャッシュについて
例として上記のようなシンプルなwebシステムがあるとします。このシステムでキャッシュを使える場所は、次の表のとおり3つあります。
番号 | キャッシュの種類 | 働く場所 | 特徴 |
1 | ブラウザキャッシュ | クライアントのブラウザ | ・ブラウザがクライアントのローカルにキャッシュを保存する ・明示的な設定なしでもブラウザはキャッシュする。どの程度、どの時間キャッシュするかはブラウザに依存する |
2 | CloudFrontキャッシュ | CloudFront | CloudFrontが、エッジロケーションというクライアントに地理的に近い場所からキャッシュしたコンテンツを配信する |
3 | FastCGIキャッシュ | Nginx(web server) | Nginxがserver上にキャッシュを作成する |
基本的な考え方として、高速化するためには可能な限りクライアントに近い場所でキャッシュを使える状態を作ることが重要です。上記の例では、優先度的にブラウザキャッシュ、CloudFrontキャッシュ、FastCGIキャッシュの順でキャッシュを使えるように設計するとよいでしょう。
BacklogのWordPressウェブサイトでは下記の通り、FastCGIキャッシュ以外は設定されていなかったので、それらを設定しようと考えました。
キャッシュの種類 | 働く場所 | 設定状況 |
ブラウザキャッシュ | クライアントのブラウザ | 設定無し |
CloudFrontキャッシュ | CloudFront | 設定無し |
FastCGIキャッシュ | Nginx(web server) | 設定済み |
CloudFrontとは?
ここまで説明無しにCloudFrontと書いてきましたが、改めてCloudFrontについて説明します。既にご存知の方は本項を飛ばして、次の項まで進んで頂けたらと思います。
CloudFrontは、コンテンツの配信を高速化するサービスです。CloudFrontは、配信コンテンツが実際に格納されているサーバー(オリジンサーバー)ではなく、エッジロケーションと呼ばれるクライアントに地理的に近い場所からコンテンツを配信します。
クライアントがCloudFrontでキャッシュ設定したコンテンツへのアクセスをリクエストすると、CloudFrontはオリジンサーバーから該当コンテンツを取得し、エッジロケーションに指定された期間だけ保存します。それ以降、クライアントはエッジロケーションから指定された期間、該当コンテンツを取得できます。
CloudFrontを使用しない場合
下記のような図になります。クライアントはリクエストのたびにEC2(オリジンサーバー)にアクセスします。
CloudFrontを使用する場合
下記のような図になります。
CloudFrontにキャッシュがある場合は、クライアントはCloudFrontのエッジロケーションにアクセスします。例えば、Tokyoに住んでいるクライアントはTokyoのエッジロケーション、USA(us-east-1)に住んでいるクライアントはUSA(us-east-1)のエッジロケーションにアクセスします。
CloudFrontにキャッシュが無い場合は、CloudFrontはEC2(オリジンサーバー)にコンテンツを取りにいきます。Tokyo、USA(us-east-1)のどちらに住んでいるクライアントも、EC2(オリジンサーバー)からコンテンツを受け取ることになります。
CloudFront利用の注意点
CloudFrontを使用する際は、キャッシュする(してよい)ファイルとキャッシュしない(してはいけない)ファイルを区別して適切な設定をすることが重要です。万が一、個人情報を含むファイルをCloudFrontがキャッシュして他の訪問者に配信したら、個人情報の漏洩に繋がります。
それでは、実施したことについて説明していきます。
ブラウザキャッシュの設定
明示的な設定無しでもブラウザはキャッシュを作成しますが、ここでは明示的にキャッシュするファイルとキャッシュ時間を設定することで、キャッシュ体験をコントロールし、表示速度短縮を狙います。
具体的な設定ですが、Nginxのlocation設定で、静的ファイルに対して Cache-Controlヘッダーを追加しました。Googleの開発者ドキュメントに従って、キャッシュ保持期間(max-age)は1年にしています。後述のCloudFrontでのキャッシュ時間を2日にするために、s-maxageは2日にしています。
例: location ~*\.(jpg|jpeg|gif|png|css|js|webp|mp4)$ { add_header Cache-Control "s-maxage=172800,max-age=31536000"; }
ブラウザに長期間キャッシュを保持させる際には、「名前を変えて対象ファイルを更新しないと、すでにキャッシュを持ったブラウザでは変更が反映されない」といった注意点があります。「対象のファイル更新時は名前を変える必要がある」ことをWebsiteコンテンツを更新するチームと事前に認識を合わせました。
CloudFrontでのコンテンツ配信
キャッシュの設計
設定を実施する前に、どのようにキャッシュするかを設計しました。具体的には、どのファイルをどの程度の時間キャッシュするかなどを整理し、スプレッドシートにまとめました。下記がその例です。
キャッシュ可否 | パスパターン | キャッシュ時間(s) | キャッシュキー | キャッシュ ポリシー |
オリジンリクエストポリシー |
否 | */wp-admin/* | なし | なし |
CachingDisabled |
AllViewer |
否 | *.php | なし | なし |
CachingDisabled |
AllViewer |
可 | ja/git-tutorial/*.js | 172800(2d) | クエリ文字列: すべて |
CachingOptimized |
AllViewer |
可 | ja/git-tutorial/*.png | 172800(2d) | クエリ文字列: すべて |
CachingOptimized _for_assets_on_backlog _static_site |
AllViewer |
可 | app/themes/backlog-child/assets/* | 172800(2d) | クエリ文字列: すべて |
CachingOptimized _for_assets_on_backlog _website |
AllViewer |
可 | ja/ | 300 | クエリ文字列: すべて cookie: wordpress_logged_in* , wp-settings* |
CachingOptimized _for_docs_ja_on_backlog _website |
AllViewer |
まずは、キャッシュしてはいけないファイルとキャッシュしたいファイルのパスパターン(ドメイン以降の任意の文字列)を洗い出します。動的なコンテンツはユーザーごとに振る舞いが変わるので、キャッシュしないようにします。例えば、WordPressの管理画面やPHP、そしてAmazonの決済画面などユーザーごとに情報が違うWebシステムなどです。キャッシュしたいファイルは静的なコンテンツです。今回は画像や動画やjsやcss、一部のHTMLをキャッシュするように設計しました。
次にキャッシュ時間やキャッシュキーですが、SCSKさんの「Amazon CloudFrontとキャッシュ制御の基礎」を参考にさせてもらいました。キャッシュ時間は、前述の記事とコンテンツの更新頻度を考慮して決めています。キャッシュキーは、クエリ文字列、HTTP Header、およびCookieの値から選ぶことができ、選んだ値を含むリクエストが発生した時にCloudFrontはキャッシュをクライアントに返します。基本的にクエリ文字列は ”すべて ”にしており、クエリ文字列を含んだコンテンツごとにキャッシュするようにしています。これを書いている現在、画像などの静的コンテンツはキャッシュキーを ”なし” にした方がよかったかもと思いますが、当時はほぼ90%を超えるキャッシュヒット率(キャッシュしたコンテンツをリクエストした割合)を目にして許容範囲と考え、”すべて”のクエリ文字列を含んだままにしました。。
キャッシュポリシーとは、キャッシュ時間やキャッシュキーの情報を含むポリシー設定です。名前をつけて独自に作成し、設定の段階でパスパターンごとにポリシーをアタッチすることができます。
オリジンリクエストポリシーは、クライアントのリクエスト時にキャッシュがなかった場合に、CLoudFrontがオリジンサーバーにコンテンツをリクエストする際に含まれる値をまとめたポリシーです。キャッシュポリシーと同じく、設定できる値はクエリ文字列、HTTP Header、およびCookieの値です。今回の設定では、リクエストに含めたくない情報は特に無いので、全ての情報をオリジンにリクエストするマネージドな設定である ”AllViewer” を使用しています。
この辺りの説明は専門用語が出てくることもあり、説明が難しく文章が長くなりがちです。より正確で詳細な理解をしたい方は、公式ドキュメントのURLを記載しているので、そちらからご確認ください。
注意点
余談ですが、私がハマったことを書きます。
CloudFrontのオリジンをALBにして証明書を利用する際、Host Headerをオリジンに転送しないと502エラーになるのでご注意ください。回避方法は次の2種類があります。
- キャッシュポリシーのキャッシュキーにHost Headerを含める。
- オリジンリクエストポリシーのオリジンリクエストにHost Headerを含める。”AllViewer” だとHost Headerも含まれるので、こちらでもOKです。
不要プラグインの削除
インストールされていますが、有効化されていない、または有効化されているが実際には使われていないWordPressプラグインが5つありました。
こちらの記事「WordPressを爆速チューン!不要なプラグインは削除しよう」では、Akismetというスパム防止のプラグインを止めるだけで200ms速くなった!、とありました。使われていないプラグインの中にそのAkismetがあったので、心を踊らせながら止めました!
成果
上記を設定したところ、下記の通り、Pagespeed Insights、Pingdomといった評価サイトのScroreが改善しました。特に、Pingdom で測定した表示速度は6.5秒から1.45秒と大幅に改善しました。GTmetrix の事前評価は取り忘れましたが、事後評価はA評価と良い状態です。Websiteのコンテンツも日々改善されているので、全てこの施策の成果とは考えていませんが、Pingdom の表示速度の評価は、この施策が貢献した割合が高いと考えています。(残念ながら、2023年12月14日現在は下記を下回る評価となっています。)
1. PageSpeed Insights score
Before (2023/4/19)
device | Result | Performance(%) |
desktop | success | 75 |
mobile | failure | 41 |
After (2023/9/29)
device | Result | Performance(%) |
desktop | success | 82 |
mobile | success | 42 |
2. Pingdom score
Before (2023/4/19)
Performance grade | Load time(s) |
64 | 6.48 |
After (2023/9/26)
Performance grade | Load time(s) |
66 | 1.45 |
3. GTmetrix score
Before
取り忘れました。
After (2023/9/29)
GTmetrix Grade | Performance | Structure | Largest Contentful Paint | Total Blocking Time | Cumulative Layout Shift |
A | 96% | 88% | 1.0s | 53ms | 0 |
最も効果があったのはCloudFrontの利用でした。次にブラウザキャッシュの設定です。不要プラグインの削除は、残念ながら改善に貢献しませんでした。。
今後の課題
今後の課題としては、まずはキャッシュヒット率の改善です。前述した通り、画像などの静的コンテンツのキャッシュキーは、クエリ文字列を”すべて”にしているので、これを変更することでキャッシュヒット率が改善するかもしれません。
もう一点、ALB以下のリソース一式を現在のus-east-1リージョンからap-northeast-1リージョンに移設することも課題として残っています。日本向けのウェブサイトでありながらus-east-1で作られているので、ap-northeast-1に配置するよりも基本的に表示速度が遅いです。表示速度はCloudFrontのキャッシュでカバーしましたが、キャッシュ設定をしていないファイルの表示速度やコンテンツなどのデプロイの速度は改善の余地があります。ただし、大掛かりな作業になるので、事前に計画して実施する必要があります。
まとめ
BacklogのWordPressウェブサイトの表示速度を速くしたいと思い、ブラウザキャッシュの設定、CloudFrontでのコンテンツ配信、不要プラグインの削除を実施しました。その結果、Pingdom で測定した表示速度を6.5秒から1.45秒へ速くするなど改善効果を得られました。課題は残っているので、機会を見つけて対策していけたらと思います。