新卒2年目がCore Web Vitalsの改善で試したこと

本記事はエムティーアイ [Advent Calendar 2021](https://qiita.com/advent-calendar/2021/mti) の15日目の記事です。
# はじめに
現在新卒2年目で、普段はwebサイトの開発、保守、運用を担当しています。
今年の6月中旬からGoogleの検索ランキングシステムの仕様が変わり、Core Web VitalsというWebページのUX指標が考慮されるようになりました。（参考：https://developers.google.com/search/blog/2021/04/more-details-page-experience ）
[サーチコンソール](https://search.google.com/search-console/welcome?hl=ja) や [PageSpeed Insights](https://pagespeed.web.dev/?utm_source=psi&utm_medium=redirect&hl=ja) で確認が可能なのですが、担当している複数のサイトで良好のページが少なく、改善が必要な状態でした。
本記事では3月〜6月にかけて対応したCore Web Vitals改善施策で特に効果があった施策について記載します。

最終的に全てのページで良好になりました：![スクリーンショット 2021-06-14 10.34.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312111/02c9ee87-c6bc-9dad-25fe-8de74ae98960.png)

# Core Web Vitals について
以下の3つの要素で構成されています：
【LCP（Largest Contentful Paint）】
　最も大きなコンテンツ（画像・動画の初期表示画像・背景画像のある要素・テキストを含むブロックレベル要素など）が表示されるまでの時間を表す。

【FID（First Input Delay）】
ユーザーが最初にページ内でアクション（クリック・タップ・テキスト入力など）を行った際に、ブラウザがその操作に反応するのにかかった時間を表す。

【CLS（Cumulative Layout Shift）】
ユーザーが意図せぬレイアウトのずれがどれぐらい発生したかを表す。

詳しくは下記参考ページを参照してください。
・https://developers-jp.googleblog.com/2020/05/web-vitals.html
・https://web.dev/vitals/
・https://web.dev/lcp/
・https://web.dev/fid/
・https://web.dev/cls/

担当しているサイトでは上記すべての指標で「改善が必要」もしくは「不良」となっていました。
![スクリーンショット 2021-03-29 13.35.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312111/b6b01b8e-2da2-5bf6-bda9-5de3402cd2a8.png)
[PageSpeed Insights](https://pagespeed.web.dev/?utm_source=psi&utm_medium=redirect&hl=ja) や [WebPageTest](https://www.webpagetest.org/) を用いて調査したところ、**「サーバーの応答速度が遅い」**ことと**「画像や広告表示後にレイアウトがずれる」**ことの影響が大きいことがわかりました。
以降はこれらの改善方法について記載します。

# サイト応答速度改善
TTFB(Time To First Byte)で計測しました。
担当している他のサイトではTTFBが100ms~300ms以内であるのに対し、今回のサイトでは **平均780ms** と倍以上の時間がかかっていました。
こちらを改善することで、LCPとFIDが改善するだろうと予想して調査を進めました。

サイトはAWSで運用しています：
・AWS CloudFront（キャッシュは画像のみ）
・WordPress ([KUSANAGI for AWS](https://kusanagi.tokyo/cloud/kusanagi-for-aws/))
・AWS RDS

前任の方に話を伺ったところ、WordPressのテーマを色々カスタマイズしており、不要なコードも多くあるという情報を共有頂きました。

また、ブログ記事公開時にアクセスが集中 → RDSのCPU使用率が急上昇していました。
つまりページ表示ごとにクエリを発行しており、キャッシュは一切使用していない状態です。
[Query Monitor](https://ja.wordpress.org/plugins/query-monitor/) というWordPressのプラグインを用いてページ表示時に実行されているクエリを確認したところ、クエリ数：208、クエリ実行時間：250msとなっていました。

現時点で対応できそうなことを挙げてみると、
・不要なコードの削減
・クエリの最適化
・ページキャッシュを使用する
ただし、「担当し始めて2~3ヶ月目でまだ全体を把握し切れていない」ことに加えて「担当するまでPHPやWordPressを触ったことがない」状況でした。

工数を考えると、選択肢がキャッシュしかない。。

## ページキャッシュの有効化
実装方法については以下の3つが考えられました。
・CloudFrontを使用する
・KUSANAGIのfcache, bcacheを使用する
・WordPressのプラグインを使用する

CloudFrontでキャッシュの設定をしてみたところ、キャッシュがHITする時としない時が発生してしまいました。
原因がよく分からないまま時間が過ぎていき、広告が怪しそう、、というのが分かったところで切り上げました。

プラグインはWordPressのバージョンの問題等があり、最終的にKUSANAGIのfcacheを使用しました。
有効にする方法は簡単で、以下のコマンドを実行するのみです。キャッシュクリアも簡単にできます。

```bash
# fcache有効化
$ kusanagi fcache on

# キャッシュクリア
$ kusanagi fcache clear
```

その他変更した設定周りは以下の2点です：
・キャッシュの有効期限がデフォルトで10時間になっていたので、企画側と相談して30分間に設定しました。（ランダムで選択される関連記事の更新のため）

```nginx:/etc/nginx/nginx.conf
# 変更前
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=wpcache:30m max_size=512M inactive=600m;
# 変更後
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=wpcache:30m max_size=512M inactive=30m;
```

・記事公開時や編集後には変更をすぐに反映する必要があったので、記事公開時にキャッシュクリアをするコードを追加しました。

```php:DocumentRoot/wp-content/mu-plugins/kusanagi-core/modules/cache-clear.php
add_action( 'transition_post_status', array( $this, 'post_publish_clear_fcache' ), 10, 3 );

public function post_publish_clear_fcache( $new_status, $old_status, $post ) {
	if ( $new_status == 'publish' ) {
             $this->clear_fcache();
	}
}
```

最終的にTTFBが **平均780ms → 平均71ms** まで改善しました。
それに伴ってLCPが **9.6s → 6.5s** まで改善しました。
FID（lighthouseでは計測できず、TBTで代用）に関しては2014ms → 2570msと増える結果になりました。
※FIDについてはjsの最適化（不要なjsを読み込まない、使用していないコードを削除など）が有効でした。

参考：
・https://kusanagi.tokyo/document/kusanagi_plugin/
・https://www.zukeran.org/shin/d/2018/05/15/kusanagi-bcache-fcache-2/
・https://garigaricode.com/kusanagi_vs_wordpress/#go-to-6-e8b2a0e88db7e38386e382b9e38388
・https://wpdocs.osdn.jp/%E6%8A%95%E7%A8%BF%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%81%AE%E9%81%B7%E7%A7%BB

## キャッシュ使用前後比較
キャッシュ使用前後をgoogle analyticsで確認してみました。
本サイトの特徴として、記事公開時にPV（ページビュー）数が増えており、アクセスは新規記事に集中します。
キャッシュ使用前（特に2月）はPV数が多いとRDSのCPU使用率が増えて処理が追いつかず、応答速度も遅くなっていました。
キャッシュ使用後（4月中旬以降）はPV数が多いほど応答速度も速くなっています。
※今回のサイトでは同じページにアクセスが多いので、キャッシュを有効活用できました。
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312111/a722b203-e3d4-8954-ff03-bb24dafafbbf.png)

## キャッシュ使用による副次的効果
以前までは記事公開時にRDSのCPU使用率が急上昇していましたが、fcacheを使用するようになってからCPU使用率が下がりました。
しばらく運用した後にスペックを下げても問題ないことを確認し、インスタンスタイプを変更してサーバーコスト費用削減もできました。

ユーザーは読み込み速度が速くなってwin。
SEO対策としてのLCPが改善できてwin。
サーバー負荷軽減とコスト削減ができてwin。
まさにwin-win-winですね。

# CLS改善
PageSpeed InsightsやlightshouseでCLSが発生している要素を確認することができます。
今回のサイトでは画像や広告表示後にレイアウトがずれていました。
そこで表示前に予め表示枠を確保しておき、表示後のずれを無くしました。

・画像の場合：imgタグにwidth, heightを追加

```html
<!-- 変更前 -->
<img src="hoge.png">
<!-- 変更後 -->
<img src="hoge.png" width="300" height="300">
```

※wordpressの場合は「[wp_get_attachment_image_src()](https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/wp_get_attachment_image_src)」を使用して画像のサイズを取得できます

・広告の場合：広告をdivタグで囲い、min-heightで高さを確保しておく。
※複数の広告サイズがある場合は、一番大きいものに合わせて高さを設定しておくとCLSは0にできる。
※ただし配信される広告によっては無駄な余白が気になったりするので、企画側と相談する。

```html
<!-- 変更前 -->
<script type="text/javascript" src="ad.js"></script>
<!-- 変更後 -->
<div style="min-height: 100px">
    <script type="text/javascript" src="ad.js"></script>
</div>
```

最終的に**全てのページでCLSが0.1以下**になりました。（平均0.06）

# その他の施策
今回の記事には詳細は記載しませんが、以下の施策も実施しました。
・jsの最適化（不要なjsを読み込まない、使用していないコードを削除など）
・画像のリサイズと圧縮
・画像の非同期読み込み
・不要なcss削除（スタイルが重複しており、4万行→1万行弱まで頑張って減らした）

# おわりに
PageSpeed Insightsのシミュレーション結果では数値が悪くても、実機では問題ないことが多いので、計測方法や目標をしっかり定めてから始めることをオススメします。

2年目になり、一通りのタスクをこなせるようになってきてから初めての大きなタスクでした。
最初はCore Web Vitalsを知らない + PHP触り始めた状態から始めたのですが、最終的に全てのページで良好になるという結果を残せて良かったです。
フロント〜インフラまで一通り見れるようになってきたことや、知らないことに対する調査能力に関して一歩成長できた気がします。

長文にお付き合いいただき、ありがとうございました。