【PageSpeedInsights】分析方法と改善方法 （LCP改善で38点→97点へ）

# LCPのスコアが最重要
3年間スコアに悩まされてましたが、集中的に改善を行い97点まで上げました。
基本的なウェブサイトの分析方法を知り、着実に改善する方法をまとめます。

振り返ると LCP のスコアが最重要項目でした。
他の指標は LCP のスコアを上げるための補助項目な側面が多いため、LCPに着目して改善しましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/65773f54-0a46-f630-7fd2-35591b5a0455.png)

## PSI改善の基礎が解ってる人は <a href='#tada-レンダリング改善-tada'>レンダリング改善</a>をご覧ください
他の項目はみなさん見飽きてることでしょう
基礎がわかってる人は<a href='#tada-レンダリング改善-tada'>レンダリング改善</a>をご覧ください。



## スコア改善の順序
スコア改善はこの順番でやると挫折しないのでおすすめです。

* <a href='#airplane_departure-ファイル容量の削減-airplane_arriving'>ファイル容量</a> （多分もうみなさん恐らくやってますよね！）
* <a href='#speedboat-サーバ応答速度改善-speedboat'>サーバ応答速度</a> （ここもN+1とかも恐らくやってますよね！）
* <a href='#tada-レンダリング改善-tada'>レンダリング</a> → ここが課題

今回の記事ではこの3点を軸に改善方法をまとめていきたいと思います。

恐らくファイル容量とサーバの処理見直しは基本的な部分ですし取り掛かりやすいので改善済みな人が多いと思いますし
解説記事も多く出ていると思うのでざっくり私たちの対応している内容だけまとめます。

#  :airplane_departure: ファイル容量の削減 :airplane_arriving: 
これが最も気軽に改善できますよね

しかし、これを改善しないとサーバ応答速度やレンダリングを改善したとしてもスコアにうまく反映されず
「努力してるのにPageSpeedのスコアが上がらない！」という状況に陥り挫折します。

まず、サイトから配信しているコンテンツの容量を改善しましょう。

画像は全て、webp、moz jpegなど次世代フォーマットへ変換し
適切に改善がスコアにつながる状態を作りましょう。

CSS、JSの圧縮は開発しているフレームワークごとに適切なものを選びましょう。

### 【画像の圧縮手順】
画像の圧縮は最重要なので解説を入れます。

OSSで開発されている画像圧縮が可能なサイトがあるのでこちらで圧縮してしまいましょう。
エンジニアなんだからプログラムで圧縮したい気持ちはわかります。

画像圧縮はサイズ、画質、型式の3要素での圧縮が必要です
各画像の役割、表示サイズによって求められる画質が変わるので目視で極限まで下げましょう

https://squoosh.app/


### 【webpって使えるの？】
webp型式や次世代フォーマットも今となっては対応していないブラウザはIEくらいで
ほぼモダンなブラウザは対応しているので積極的に取り入れましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/e90624c8-5c07-aba5-adb3-64998f24d8b2.png)
引用：https://caniuse.com/?search=webp

対応していないブラウザに対応するためのpictureタグなどもありますが
今となってはその対応も不要だと考えてます。

webp形式でも通常の <img src=''> というタグで表示できるので表示してみてください
問題なく利用できると思います。


## CDNの利用
もし利用可能であればCDNの利用も検討してみましょう
わずかに速度が上がると思います、しかし必須ではないと感じてます。

AWSのCloud Frontを利用していますが、利用前と利用後で大きくスコアが変化したということもないので
画像の圧縮が一番重要だと考えてます。



#  :speedboat: サーバ応答速度改善 :speedboat: 
これはフレームワークごとに色々だと思うので発行されているSQLなどを見て
改善ポイントを探したりしましょう

### RailsでできるSQL改善
* rails s コンソールで 1ページ開くときに、流れるSQLを全てコピーして重複しているものを探す
* N+1を探すツールで見つけて改善する（ChatGPTに投げれば改善してくれます）


### slim erb haml などのテンプレートエンジンのレンダリングコスト
テンプレートエンジンのレンダリングはSSR（サーバサイドレンダリング）されているため
サーバの応答速度に影響してきます。

意外と見落としがちなので、改善できそうな部分は改善しておきましょう

```erb:全商品をeachでレンダリングするとerbからHTMLを作る処理に時間がかかる
<% @products.each do |product| %>
  <img src='product.image'>
  <%= product.name %>
  ......
  // 商品パーツのレンダリング処理
<% end %>
```

これらのレンダリングを毎回行う必要がない場合はキャッシュしてしまいましょう

```
<% cache(@products) do %>
<% end %>
```

### キャッシュの種類
* ページキャッシュ
* アクションキャッシュ
* フラグメントキャッシュ
* ロシアンドールキャッシュ


# :tada: レンダリング改善 :tada:
2023年4月時点私が改善する上で PageSpeedInsights のスコアの測定基準は LCP が最重要項目でした。
Largest Content をどれだけ高速でレンダリングできるかというところをまず注目しましょう

```txt:各採点項目の解説を一応載せておきます
【FCP】 最初にコンテンツがレンダリングされたタイミング
【LCP】 一番大きいコンテンツがレンダリングされたタイミング（FCPがLCPであることが望ましい）
【TBT】 LCP をブロックしている処理の秒数 （LCPが改善すれば自動的に100点になる）
【CLS】 コンテンツの移動量
【SpeedIndex】 ページが読み込まれるまでの速度
```

## 状態ゴール： FCP = LCP を目指す
TBTはLCPをブロックしている処理の合計秒数なので
Largest Content Paint これが非常に重要です

FirstContentPaint で LargestContent が読み込まれればサイトのスコアは一気に上昇します。

3G低速回線でページを読み込んだときに最大コンテンツ判定されているパーツが
最初にレンダリングされる状態まで持っていきましょう。

### SpeedIndex と CLS が0点だったとしても 67点を目指せます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/be1d42e3-2430-e002-03d5-590efe511bf3.png)


## 【LCPのパーツはどれなのかを理解し最速化する】

SEOを重点的に上げたいページに配置されている Largest Content をまず探しましょう。
探し方は簡単で、PageSpeedInsightsでスコアを計測するとページの下の方に書いてあります。

CMD + F で最大コンテンツの描画 を検索しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/e338bfb4-309b-2df7-3e85-2cb40ea6c607.png)


最大コンテンツが特定できたらそのパーツが最速で読み込まれるようにしましょう。
FirstContentPaint で LargestContent がレンダリングされるようにしてしまいましょう。

このとき分析するために使えるツールがChromeの3つのタブです

## LCPを手元で計測する方法
パフォーマンスタブで、FP、FCP、LCPを確認することができます。
FCPとLCPの間で行っている無駄な処理を特定し、その全てを取り除きましょう。

最速でLCPのパーツをレンダリングしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/3434cb0d-900f-46bf-ccb3-47812281f288.png)


### 【理想的なレンダリング】
理想的なレンダリングはLCPパーツのレンダリングに必要なリソースのみをロードする状態です
私のサイトの場合は以下のファイルだったのでそれだけ読むように変更しました。

* HTMLのダウンロード
* LCP画像だけ優先的にロード、CSSとカスタムフォント、js（同期読み込み必須）のロード
* コンテンツレンダリング開始（ここでLCPパーツが即時にレンダリングされる）


`fetchpriority: 'high'` を画像につけて、LCP画像を最優先でロードさせることができます

現在使える様になりました！ :tada: 
https://developer.mozilla.org/ja/docs/Web/API/HTMLImageElement/fetchPriority

画像容量よりもCSSの方が容量が大きいため
全体ロード時間に影響を与えることなくLCP画像のロードを並行して行い
最初に表示されるコンテンツがLCPパーツになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/e81941cc-23ef-f74d-4f82-d0f5ab64424f.png)


### 【LCPパーツレンダリングに時間がかかってるサイトの例】
* HTMLのダウンロード
* CSS、WPプラグイン、Boostrap読み込み、その他色々 （TBT発生、LCP増加）
* LCPに関係ない画像の読み込み、計測jsの読み込み （TBT発生、LCP増加）
* LCPの画像の読み込み開始（容量大）
* LCP画像のレンダリング完了

HTMLダウンロードが完了した後HTMLの上に書かれているものから順に読み込みを行いますが
LCP画像のレンダリングの前に不要なjsの実行やパーツのレンダリングが多く含まれているため
LCP判定されている画像の読み込みが遅くなってしまっています。

LCP画像をレンダリングするのに不要なjsやパーツのレンダリングをLCP画像のレンダリング前行うと
それが全てTBT（TotalBlockTime）に加算されてしまいます。

LCPパーツをレンダリングすることをブロックした処理の時間の合計がTBTだと考えてもらうとわかりやすいかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/185131b3-250a-a2d9-8e4f-77eee85dee76.png)

引用： https://odorate.co.jp/

## 【ネットワークタブで読み込み優先度を確認する】
LargestContentがどのタイミングで読み込まれているかを確認しましょう。
今回のサイトでは LargestContentは画像でした

HTMLファイルが読み込まれ、内容を解析し、優先的に読み込むべきファイルをブラウザが決定します

### 【LCPパーツを最速で表示する場合の理想的な流れ】
今回スコアを上げたサイトでは、現状以下のような流れでレンダリングされてます

1. HTMLファイルの読み込み
最初にHTMLファイルが読み込まれ、ページの基本的な構造が確立されます。
2. 優先ロードリソースの決定
HTMLを解析する過程で、ブラウザはCSS、JavaScript、画像など、どのリソースを優先的にロードするかを決定します。
この時にLCPパーツに不必要で、時間のかかるものを読み込まない様にします。
3. 必要なリソースの優先ロード
ブラウザの優先度に基づき、CSS、JavaScript、LCPに影響する画像、そしてフォントがロードされます。
4. 意図的なレンダリングブロック
ページが一度に綺麗に表示されるよう、必要なリソースが完全にロードされるまでレンダリングを一時的にブロックし
LCPパーツがレンダリングできるタイミングになったらブロックを解除します。
5. ロード完了後のレンダリング開始
最低限LCPパーツをレンダリングするために必要なリソースがロードされた後
ページのレンダリングを開始する


### 【Preload　画像の読み込み優先度を指定する】

<link rel="preload"> タグを使うことで、特定のリソースがページに必要だとブラウザに伝え、優先的にダウンロードするように指示できます。

LCPパーツのレンダリング完了までの時間が非常に重要となるため
LCPパーツが画像の場合は優先的に読み込む様にブラウザに伝えてあげましょう。

```html:Preload
<head>
  <link rel="preload" as="image" href="example-image.jpg">
</head>
```

### 【fetchpriority: 'high'】
使える様になりました！ :tada: 
https://developer.mozilla.org/ja/docs/Web/API/HTMLImageElement/fetchPriority

LCP パーツの画像には fetchpriority: 'high' をつけて最速で読み込んでます

javascriptやcssは defer をつけたり、遅延ロードしない限り優先的にロードされてしまいます
FCPレンダリング時に不要なリソースは読み込まないようにしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/2a1b2df4-3360-e8c2-680a-fc0ac4254822.png)

## LCP画像をcssのbackgroundで指定して居る場合
css の background で呼ばれてる画像などは、css の解析が終わらないと読み込みを開始してくれないため
html側に fetchpriority を書くのが有効です。

backgroundで指定してあった場合は、必ず指定しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/8fca0fd5-b862-4ca8-943f-d094ed170bda.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/ab0c2bc4-f993-4c30-ae00-4bfe7b5ff98f.png)


### 【必要なレンダリングブロックもあります】
HTMLが読み込まれているものの、CSSがまだ読み込まれていない状態でレンダリングを行う場合
FCP（First Contentful Paint）は速くなりますがCSSが読み込まれた瞬間にページ全体の見た目が大きく変わり、再レンダリングが必要になってしまいます。

その結果、CLS（Cumulative Layout Shift、累積レイアウトシフト）が発生しユーザーの意図しないクリックなどを誘発してしまいます、さらに、デザインが当たっていない素のHTMLが一瞬表示されサイト自体の信頼性にも関わります。

そのため、レンダリングブロックは意図的にブラウザが行なってくれて居ます。
具体的には、CSSなど必要なリソースが全てダウンロードされるまでページのレンダリングを一時停止（ブロック）する様な処理です

レンダリングブロックは一概に悪とは言えませんがこのような事情があるため、
レンダリングブロックが適切に行われているかどうか、チェックする必要があります。

### 【パフォーマンス分析情報タブ（TBTとレンダリングブロック解析に利用）】
次はレンダリングブロックを行っているリソースを手軽に確認する方法です。
パフォーマンス分析情報タブを利用すると簡単に探すことができます。

基本表示されていない方もいるかもしれませんが、3点メニュー、その他ツールのパフォーマンス分析情報から確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/3a14d746-a638-a10e-fc85-4f9ad7b88176.png)

ページ下部で紹介している、「PageSpeedInsightsと同じネットワーク速度のプロファイル」を設定し、読み込みを計測しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/6896d9d6-a87a-e34d-23cd-faae49666891.png)

## パフォーマンス分析の見方
読み込みを計測するとこのような画面になると思います。
具体的にみていこうとおもいます。

### 高速化できているサイトの場合
FCPとLCPの間に何の処理もなく、同時にレンダリングされており
レンダリングブロックリクエストにカーソルを合わせてファイルを確認しても
LCPパーツをレンダリングするのに必須なファイルだけになっています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/3612ebae-062f-cdcb-3283-fbb54f283b8d.png)


### 課題があるサイトの場合
FCPとLCPの間に多くの処理があり、レイアウトのシフトもあり
`さらに表示（17）` のように別のタスクが多く挟まっている

また、FCPの前に読み込んでいるjsやcss、画像が多く `さらに表示(37)` 個ほど存在し 
FCPのスコアにも大きく影響を与えている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/5fbdabbf-552b-884d-2436-881441edb164.png)

### 【改善方法】
1つ1つのファイルを確認し、本当に優先的に読み込む必要があるのかを考え

HTMLのコードのファイル呼び出し順を変更しては計測してを繰り返し
最適な読み込み順になるまでそれを繰り返しましょう。

遅延読み込みを行ったり、モーダルなどの隠しておいても良い要素はフッターでレンダリングしたり
細かい対応を行う必要がありますが、それはサイトごとに異なるのでさまざまな情報を参考に進めてみてください。

# スピード感を持って改善するための事前準備（おすすめ）

## PageSpeedInsightsと同じネットワーク速度のプロファイル
ネットワークタブでは、意図的にネットワーク速度を遅くし
どのようにレンダリングされ、レンダリングブロックが発生しているか確認しやすくする機能があります。

> このネットワーク速度次第でレンダリングブロックが発生したり、しなかったりするので
> PageSpeedInsightsが計測時に行ってる低速化環境と手元の計測環境を揃えることは必須の設定です。

PageSpeedIngishtsでのネットワーク速度は 4G低速と書いてありますが、具体的には以下の通りです。

```
ネットワーク スロットリング
150 ms TCP RTT,
1,638.4 Kbps throughput
```

自分でPageSpeedInsightsと同じ設定を作り、これを設定しながら改善しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/d75274f9-cc79-0f95-353c-91e3b09d5076.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/caef9484-2864-30d7-6bfd-242b77de6751.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/00a487d6-7956-e696-2117-4ed6c4a9f768.png)


## 本番環境と同等の環境を用意
サービスを production 環境同等の状態で動かせるように準備をしましょう。

pagespeedingisht.example.co.jp などのサブドメインを一時的に作成してチェックでも良いと思います。
ローカル環境で計測をしても、webpackなどでコンパイルされたファイルかどうかなど本番との差分が多く正常にスコア計測ができない可能性があるので、本番同等の計測サーバを用意することをお勧めします。

```sh:再起動して動作確認
ps aux | grep master | grep unicorn | awk '{print $2}' |  xargs kill -9 & bundle exec unicorn_rails -c /var/www/example.co.jp/current/config/unicorn/production.rb -E production -D
```


# 実際に行った改善施策の紹介
以下は30点から97点まで持っていったときにやったこと一覧です。
サイトによって違うと思いますが参考になればと思います。

## 画像のwebp変換をマニュアル化（人力で判定）
運用フローを変更し、画像を全てwebpに変換しました。
ただ形式をwebpにしてもあまり意味がないので
コンテンツチーム（アルバイトさんなど）に画像圧縮方法を共有しマニュアル化

全てのアップロードする画像を圧縮してからアップロードするようにしてもらいました。

しかし、これを強制することができない、お客様がアップロードすることもあるので
サーバサイドで自動的に圧縮する仕組みも入れてます。

この仕組みをインターンの子に作ってもらいました。
インターン入社最初の課題が重めでしたが、相談しつつ作りきれて
良い成長になったかもしれませんね。

以前はLambdaを用いて変換処理をして居ましたが
設定のミスと退職時の引き継ぎミスにより、高額請求が来て居たので停止して居ます。
S3のバケットアップロードを検知し、画像の形式がwebpではない場合
Lambda で変換処理を開始、webpに変換を行い読み込むと言うものです。

正しく変換されなかったとき、同じファイル形式で再度バケットにPUTされてしまい
無限ループしてしまったので、読み込むバケットと書き出すバケットは分けたほうがよかったです。


## 画像の遅延読み込みと優先読み込み
lazyloadでページ表示されたタイミングで画面に入らない画像を遅延読み込みするのはメジャーで
やっている方が多いと思いますが、優先的に読み込むファイルの指定も行いました。

fetchpriority: 'high' を画像につけることで
ブラウザがHTMLをダウンロードしたタイミングで次に読み込んでくれるようになります

ただ、これをつけるのは本当に大切な画像、LCPパーツが画像である場合だけにした方が良いです。

全体的にみたときに画像のロードが終わるまでレンダリングをブロックしてしまうことになるので
CSSやフォントの読み込みの方が早い場合は付けるかつけないか、検証してから決めましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/18619172-2ab7-b9a8-7d33-9cba26faa751.png)


## 不要な計測タグの精査と削除を月1回
サイトに貼っている計測タグやTagmanagerで入れているタグも見直してもらいましょう
ビジネスチームに確認してもらいかなり削減してもらいました。

1pxの計測タグなども結構影響していることがあるので（facebook ms）不要なものは消しましょう。

月1回ほど整理の時間を設けるのがベストだと感じております。
ちゃんと整理して利用できていれば良いですが
意図しないタイミングで追加したタグが原因でページスピードが遅くなっていることもあるので
定期的に監視してあげることが大切です。

## SQL改善
SQLに関してはN+1もそうですが
同じ処理を何度も行ってる場所があるかもしれないです。

Railsなどで1ページアクセスしたときに流れているSQLのログを一旦テキストファイルに移動して
重複している処理を探しましょう

ChatGPTに聞いてしまっても良いかもしれないですね。

### Bullet (Railsの場合）
BulletというN+1を探すGemもあるので、こちらを利用するのも良いかもしれません。

N+1を探すことができますし、多くのプロジェクトや企業で取り入れられて居ます。

https://github.com/flyerhzm/bullet

## fontawesomeのSVG化
fontawesomeもかなりロードに時間がかかってしまうもので、LCPパーツのレンダリングまでに必要になってしまうものでした。
これを読み込んでいたら速度に大きく影響があるため、SVG画像に変換して直接インラインで画像を埋め込むように変更しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/3e216ff0-fa5c-2dbb-c04a-4e9b9cbc9427.png)


## Optimizeのアンチフリッカー削除

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/b8d4b920-435d-6bc7-b481-f335aa10e95f.png)

これが大きくスコアに影響しました。

google optimize でABテストを行う場合、パーツの入れ替えが終わるまで
レンダリングをブロックし、パーツ入れ替えが完了したタイミングでレンダリングを行うというjsを入れてるかもしれません。

これの影響でLCPが大きく悪化しており、このjsを削除したことをきっかけに大幅にスコアがあがりました。

ただ、シンプルにこれを削除すれば良いという話でもなく
画像をwebpに変換したり、他のファイルの容量を減らしていないと
パーツがガタガタと読み込まれてしまいCLSのスコアに大きく影響します。

まずは webp 対応やファイルの縮小が完了してからこちらを導入するのが良いと思われます。


# LCP（Largest Contentful Paint）対策
LCP（Largest Contentful Paint）の最適化は、ウェブページのパフォーマンス向上に非常に重要です。そのためには、その他の要素の読み込みが干渉しないようにする必要があります。

## 有効な最適化手法
`モーダルの遅延レンダリング`: クリックで初めて表示されるモーダルは、ページのフッターでレンダリングすることで、初期ロード時間を短縮します。
`CSSの最適化`: 重要なCSSファイルはHTMLの上部に配置することで、ページの描画速度を向上させます。
`LCP要素の最適位置`: LCPに影響を与える要素をHTMLの上部に極力近づけることで、早くレンダリングを完了させます。
`リソースの順序調整`: LCP要素より上で読み込む処理やファイルは、可能な限り下に移動させます。

## 非推奨な施策
`非同期のGTM・GA`: Google Tag ManagerやGoogle Analyticsを非同期に設定すると、計測に影響が出る可能性があるため非推奨です。
`CLS防止タグの削除`: Cumulative Layout Shift（CLS）防止のためのタグを削除すると、ページ内要素が大きく動いてしまうことが明らかになる場合があります。
`ABテスト中のスコア`: ABテストを実施中は、スコアが若干低くなることを一時的に許容することもあります。


これらの最適化手法と非推奨な施策を考慮することで
LCPの改善と全体的なウェブページのパフォーマンス向上が期待できます。


#