Largest Contentful Paint を最適化する

LCP を分解して改善すべき主な領域を特定する方法に関するステップバイステップ ガイド。

公開日: 2020 年 4 月 30 日

Largest Contentful Paint(LCP)は、3 つの Core Web Vitals 指標の 1 つで、ウェブページのメイン コンテンツが読み込まれる速さを表します。具体的には、ユーザーがページの読み込みを開始してから、ビューポート内に最大の画像やテキスト ブロックがレンダリングされるまでの時間を測定します。

優れたユーザー エクスペリエンスを提供するには、ページアクセスの 75% 以上で LCP を 2.5 秒以下にするよう努める必要があります。

良好な LCP 値は 2.5 秒以下、低値は 4.0 秒を超えており、その中間には改善が必要です。
適切な LCP 値は 2.5 秒以下です。

ブラウザがウェブページを読み込み、レンダリングする速度には、さまざまな要因が影響します。これらの要因のいずれかで遅延が発生すると、LCP に大きな影響が及ぶ可能性があります。

ページの一部をその場しのぎで修正しただけで LCP に意義のある改善が見られることは稀です。LCP を改善するには、読み込みプロセス全体を確認し、すべてのステップを最適化する必要があります。

LCP 指標について

LCP を最適化する前に、LCP の問題が存在するかどうか、存在する場合はその問題の程度を把握する必要があります。

LCP はさまざまなツールで測定できますが、そのすべてが同じように LCP を測定するわけではありません。実際のユーザーの LCP を把握するには、Lighthouse やローカルテストなどのラボベースのツールが示す内容ではなく、実際のユーザーの状況を確認する必要があります。これらのラボベースのツールは、LCP を説明して改善に役立つ豊富な情報を提供できますが、ラボテストだけでは実際のユーザー エクスペリエンスを完全に表すものではないことに注意してください。

実際のユーザーに基づく LCP データは、サイトにインストールされたリアルユーザー モニタリング(RUM)ツールから、または Chrome ユーザー エクスペリエンス レポート(CrUX)を使用して表示できます。このレポートでは、何百万ものウェブサイトの実際の Chrome ユーザーから匿名データを収集できます。

Chrome DevTools の CrUX LCP データを使用する

Chrome DevTools の [パフォーマンス] パネルのリアルタイム指標ビューでは、ページまたはオリジンの CrUX LCP の横にローカル LCP エクスペリエンスが表示されます。

Chrome DevTools のパフォーマンス パネルのローカル LCP とフィールド LCP
Chrome DevTools の [Performance] パネルのローカル LCP とフィールド LCP

フィールド データを [パフォーマンス] パネルに重ねることで、ページに実際のユーザーの LCP に関する問題があるかどうかを評価し、ローカル環境の設定を調整して、問題を再現してデバッグしやすくなります。

PageSpeed Insights の CrUX LCP データを使用する

PageSpeed Insights を使用すると、上部の [実際のユーザーの状況を確認する] というセクションから CrUX データにアクセスできます。ラボベースのより詳細なデータは、下部のパフォーマンスの問題を診断するというセクションで確認できます。ウェブサイトで CrUX データを利用できる場合は、必ずまず実際のユーザーデータに集中してください。

PageSpeed Insights に表示される CrUX データ
PageSpeed Insights に表示される CrUX データ。

PageSpeed Insights には、最大 4 種類の CrUX データが表示されます。

  • この URLモバイルデータ
  • この URLパソコンデータ
  • オリジン全体のモバイルデータ
  • オリジン全体のデスクトップ データ

これらの設定は、このセクションの上部と右上にあるコントロールで切り替えることができます。URL レベルで表示するのに十分なデータが URL にないものの、オリジンのデータがある場合は、PageSpeed Insights には常にオリジンデータが表示されます。

URL レベルのデータが利用できない場合に、PageSpeed Insights がオリジンレベルのデータにフォールバックする
PageSpeed Insights に URL レベルのデータがない場合、オリジン レベルのデータが表示されます。

オリジン全体の LCP は、そのページの LCP がそのオリジンの他のページと比較してどのように読み込まれるかによって、個々のページの LCP と大きく異なる場合があります。また、訪問者がこれらのページに移動する方法によっても影響を受ける可能性があります。ホームページは新規ユーザーがアクセスすることが多いため、キャッシュに保存されたコンテンツがない場合が多く、ウェブサイトの中で最も読み込みに時間がかかるページになることがあります。

CrUX データの 4 つのカテゴリを確認すると、LCP の問題がこのページに固有の問題なのか、サイト全体の問題であるのかを判断できます。同様に、LCP の問題が発生しているデバイスタイプを確認することもできます。

PageSpeed Insights CrUX 補足指標を使用する

LCP を最適化するには、First Contentful Paint(FCP)Time to First Byte(TTFB)のタイミングも使用する必要があります。これらは、LCP に関する有益な分析情報を提供できる優れた診断指標です。

TTFB は、ユーザーがページに移動し(リンクのクリックなど)、HTML ドキュメントの最初のバイトを受信するまでの時間です。TTFB が高いと、2.5 秒の LCP の達成が困難になるか、不可能になる可能性があります。

TTFB が高い原因としては、複数のサーバー リダイレクトがある、ユーザーが最も近いサイトサーバーから遠く離れている、ユーザーがネットワーク状態が低い、またはクエリ パラメータが原因でキャッシュされたコンテンツを使用できないことなどが考えられます。

ページのレンダリングが開始されると、最初のペイント(背景色など)が実行され、その後、コンテンツ(サイト ヘッダーなど)が表示されます。最初のコンテンツの表示は FCP で測定されます。FCP と他の指標の差異は非常に有益な情報となります。

TTFB と FCP の差が大きい場合は、ブラウザがレンダリングをブロックするアセットを大量にダウンロードする必要があることを示しています。また、意味のあるコンテンツをレンダリングするために多くの労力を要していることを示す場合もあります。これは、クライアント側のレンダリングに大きく依存するサイトの典型的な兆候です。

FCP と LCP の差が大きい場合、LCP リソースがブラウザですぐに利用可能ではなく、優先順位付けできない(たとえば、最初の HTML で利用可能ではなく JavaScript で管理されているテキストや画像など)か、ブラウザが LCP コンテンツを表示する前に他の作業を完了していることを示します。

PageSpeed Insights Lighthouse データの使用

PageSpeed Insights の Lighthouse セクションには、LCP の改善に関するガイダンスが記載されていますが、まず、表示速度が CrUX から提供される実際のユーザーデータと概ね一致しているかどうかを確認する必要があります。Lighthouse と CrUX の値が異なる場合は、CrUX の方がユーザー エクスペリエンスをより正確に反映している可能性があります。CrUX データに基づいて対応する前に、そのデータがページに関するものであり、オリジン全体に関するものではないことを確認してください。

Lighthouse と CrUX の両方で改善が必要な LCP 値が示されている場合は、Lighthouse セクションで、LCP の改善方法に関する有益なガイダンスを得ることができます。LCP フィルタを使用すると、LCP に関連する監査のみを次のように表示できます。

Lighthouse の LCP の改善案と診断
Lighthouse の診断と LCP を改善するための提案。

改善の機会に加えて、問題の診断に役立つ詳細情報を提供できる診断情報もあります。[Largest Contentful Paint 要素] 診断では、LCP を構成するさまざまなタイミングの内訳を確認できます。

Lighthouse の LCP フェーズ
Lighthouse の LCP 要素の内訳

次に、これらのサブパーツについて詳しく説明します。

LCP の内訳

PageSpeed Insights でこの指標を改善する方法が示されていない場合、LCP の最適化はより複雑なタスクになる可能性があります。一般に、複雑なタスクの場合は、それを小さく、管理しやすいタスクに分割し、個別に対処することをおすすめします。

このセクションでは、LCP を最も重要なサブパートに分解する方法と、各パートを最適化する方法に関する具体的な推奨事項とベスト プラクティスについて説明します。

通常、ほとんどのページ読み込みには複数のネットワーク リクエストが含まれますが、LCP の改善の機会を特定するには、まず次の 2 つを確認する必要があります。

  1. 最初の HTML ドキュメント
  2. LCP リソース(該当する場合)

ページ上の他のリクエストが LCP に影響することもあります。しかし、この 2 つのリクエスト(特に LCP リソースの開始時間と終了時間)は、ページが LCP 用に最適化されているかどうかを明らかにします。

LCP リソースを特定するには、デベロッパー ツール(前述の PageSpeed Insights、Chrome DevToolsWebPageTest など)を使用して LCP 要素を特定します。そこから、ページによって読み込まれたすべてのリソースのネットワーク ウォーターフォールで、要素によって読み込まれた URL(該当する場合)を照合できます。

たとえば、次の図では、ネットワーク ウォーターフォール図で一般的なページ読み込みからこれらのリソースをハイライト表示しています。ここで、LCP 要素ではレンダリングのために画像リクエストが必要です。

HTML リソースと LCP リソースがハイライト表示されたネットワーク ウォーターフォール
ウェブページの HTML の読み込み時間と LCP に必要なリソースを示すウォーターフォール グラフ。

ページを適切に最適化するには、LCP リソース リクエストの読み込みをできるだけ早く開始し、LCP リソースの読み込みが完了したら LCP 要素をできるだけ早くレンダリングする必要があります。特定のページがこの原則に準拠しているかどうかを可視化するには、LCP の合計時間を次のサブパートに分割します。

最初のバイトを受け取るまでの時間(TTFB)
ユーザーがページの読み込みを開始してから、ブラウザが HTML ドキュメント レスポンスの最初のバイトを受信するまでの時間。
リソース読み込みの遅延
TTFB からブラウザが LCP リソースの読み込みを開始するまでの時間。LCP 要素のレンダリングにリソースの読み込みを必要としない場合(たとえば、要素がシステム フォントでレンダリングされたテキストノードの場合)、この時間は 0 です。
リソースの読み込み時間
LCP リソース自体の読み込みにかかる時間。LCP 要素のレンダリングにリソースの読み込みが必要ない場合、この時間は 0 です。
要素のレンダリングの遅延
LCP リソースの読み込みが完了してから、LCP 要素のレンダリングが完全に完了するまでの時間。

すべてのページの LCP は、次の 4 つのサブカテゴリで構成されます。各要素の間にギャップや重複はなく、合計すると LCP の合計時間になります。

4 つのサブカテゴリを示す LCP の内訳
同じウォーターフォール図に、4 つの LCP サブカテゴリがタイムラインに重ねて表示されています。

すべてのページの LCP 値は、これらの 4 つのサブパートに分割できます。重複やギャップはありません。これらの時間の合計が LCP 時間の合計になります。

LCP を最適化する際は、これらのサブパーツを個別に最適化することをおすすめします。ただし、これらはすべて最適化する必要があります。一部に適用した最適化によって LCP が改善されず、節約した時間が別の部分にシフトされることもあります。

たとえば、上のネットワーク ウォーターフォールでは、画像をさらに圧縮したり、より最適な形式(AVIF や WebP など)に切り替えたりして画像のファイルサイズを小さくすると、リソースの読み込み時間は短縮されますが、時間は要素のレンダリング遅延のサブパートにシフトされるため、LCP は実際には改善されません。

前述の LCP の分類と同じで、リソースの読み込み時間のサブカテゴリは短縮されていますが、LCP の全体的な時間は変わりません。
リソースの読み込み時間を短くすると、LCP は減少せず、要素のレンダリングの遅延が増加します。

これが発生する理由は、このページでは、JavaScript コードの読み込みが完了するまで LCP 要素が非表示になり、その後、すべてのものが一度に表示されるためです。

この例は、LCP の最良の結果を得るために、これらのサブパートすべてを最適化する必要があるポイントを示しています。

最適なサブパート時間

LCP の各サブパートを最適化するには、最適化されたページでこれらのサブパートの理想的な内訳を把握することが重要です。

4 つのサブパートのうち、2 つの名前に「delay」という単語が含まれています。つまり、これらの時間をできるだけゼロに近づけることが重要です。残りの 2 つの部分はネットワーク リクエストを伴うため、時間がかかります。

LCP のサブパート LCP の割合(%)
Time to First Byte(最初のバイトまでの時間) 約 40%
リソース読み込みの遅延 10% 未満
リソースの読み込み時間 約 40%
要素のレンダリングの遅延 10% 未満
合計 100%

これらの時間の分類はガイドラインであり、厳格なルールではありません。ページの LCP 時間が常に 2.5 秒以内であれば、相対的な割合は重要ではありません。ただし、どちらかの「遅延」部分で不要な時間を費やしていると、2.5 秒の目標を常に達成することは非常に難しくなります。

LCP 時間の内訳を把握する方法は次のとおりです。

  • LCP 時間の大部分は、HTML ドキュメントと LCP ソースの読み込みに費やされる必要があります。
  • LCP の前に、これらの 2 つのリソースのいずれかが読み込まれていない場合は、改善の余地があることを意味します。

各部分を最適化する方法

LCP の各サブパート タイムを、適切に最適化されたページでどのように分類すればよいかがわかったところで、次は自分のページの最適化に取り掛かりましょう。

以降の 4 つのセクションでは、各部分を最適化する方法に関する推奨事項とベスト プラクティスについて説明します。影響が最も大きい最適化案から順に表示されます。

1. リソース読み込みの遅延を排除する

このステップの目標は、LCP リソースの読み込みができるだけ早い段階で開始されるようにすることです。理論上、リソースの読み込みを開始できる最短時間は TTFB の直後ですが、実際にブラウザがリソースの読み込みを開始するまでに常に遅延が発生します。

原則として、LCP リソースの読み込みは、そのページで最初に読み込まれたリソースと同時に開始する必要があります。言い換えると、LCP リソースの読み込みが最初のリソースよりも遅れて開始された場合は、改善の余地があります。

最初のリソースの後に開始される LCP リソースを示すネットワーク ウォーターフォール図。改善の余地を示しています
このページでは、LCP リソースの読み込みが、最初に読み込まれるスタイルシートのかなり後に開始されます。改善の余地があります。

一般的に、LCP リソースの読み込み速度に影響する要因は 2 つあります。

  • リソースが検出された日時。
  • リソースに割り当てられた優先度。

リソースの検出時に最適化する

LCP リソースの読み込みをできるだけ早く開始するには、ブラウザのプリロード スキャナが最初の HTML ドキュメント レスポンスでリソースを検出できるようにすることが重要です。たとえば、次のケースでは、ブラウザは HTML ドキュメント レスポンスをスキャンして LCP リソースを検出できます。

  • LCP 要素は <img> 要素であり、その src 属性または srcset 属性は最初の HTML マークアップに存在します。
  • LCP 要素には CSS 背景画像が必要ですが、その画像は HTML マークアップの <link rel="preload"> を使用して(または Link ヘッダーを使用して)プリロードされます。
  • LCP 要素は、レンダリングにウェブフォントが必要となるテキストノードです。フォントは、HTML マークアップの <link rel="preload"> を使用して(または Link ヘッダーを使用して)読み込まれます。

HTML ドキュメント レスポンスのスキャンから LCP リソースを検出できない例をいくつか示します。

  • LCP 要素は、JavaScript を使用してページに動的に追加される <img> です。
  • LCP 要素は、src 属性または srcset 属性(多くの場合 data-src または data-srcset)を非表示にする JavaScript ライブラリによる遅延読み込みを行います。
  • LCP 要素には CSS 背景画像が必要です。

いずれの場合も、ブラウザはスクリプトを実行するか、スタイルシートを適用する必要があります。通常、ネットワーク リクエストの完了を待つ必要があります。その後、LCP リソースを検出して読み込みを開始できます。これは最適な方法ではありません。

不要なリソース読み込みの遅延を回避するには、LCP リソースが HTML ソースから検出可能である必要があります。リソースが外部 CSS ファイルまたは JavaScript ファイルからのみ参照される場合は、LCP リソースを高い取得優先度でプリロードする必要があります。次に例を示します。

<!-- Load the stylesheet that will reference the LCP image. -->
<link rel="stylesheet" href="/path/to/styles.css">

<!-- Preload the LCP image with a high fetchpriority so it starts loading with the stylesheet. -->
<link rel="preload" fetchpriority="high" as="image" href="/path/to/hero-image.webp" type="image/webp">

リソースに割り当てられる優先度を最適化する

LCP リソースが HTML マークアップから検出可能であっても、最初のリソースほど早く読み込みが開始されない可能性があります。これは、ブラウザのプリロード スキャナの優先度ヒューリスティックでリソースが重要であると認識されない場合や、他のリソースがより重要であると判断された場合に発生することがあります。

たとえば、<img> 要素に loading="lazy" を設定すると、HTML を使用して LCP 画像を遅らせることができます。遅延読み込みを使用すると、レイアウトが画像がビューポート内にあることを確認するまでリソースは読み込まれないため、読み込みが遅れる可能性があります。

遅延読み込みを行わなくても、画像はレンダリング ブロック リソースではないため、ブラウザによって最初に最優先で読み込まれることはありません。優先度を上げると効果が期待できるリソースに fetchpriority 属性を使用して、最も重要なリソースをブラウザにヒントとして伝えることができます。

<img fetchpriority="high" src="/path/to/hero-image.webp">

ページの LCP 要素である可能性が高いと思われる <img> 要素に fetchpriority="high" を設定することをおすすめします。ただし、1 つまたは 2 つ以上の画像に優先度を高く設定すると、優先度の設定が LCP の短縮に役立たなくなります。

また、ドキュメント レスポンスの初期にはあってもスタイルが原因で表示されない画像(起動時に表示されないカルーセル スライドの画像など)の優先度を下げることもできます。

<img fetchpriority="low" src="/path/to/carousel-slide-3.webp">

特定のリソースの優先度を下げると、より多くの帯域幅を必要とするリソースに帯域幅を割り当てることができますが、注意が必要です。必ず DevTools でリソースの優先度を確認し、ラボツールとフィールドツールで変更をテストしてください。

LCP リソースの優先度と検出時間を最適化すると、ネットワークのウォーターフォールは次のようになります(LCP リソースは最初のリソースと同時に開始されます)。

LCP リソースが最初のリソースと同時に開始されることを示すネットワーク ウォーターフォールの図
スタイルシートと同時に LCP リソースの読み込みが開始されました。

2. 要素のレンダリングの遅延を解消

このステップの目標は、リソースの読み込みが完了した後、いつでも LCP 要素をすぐにレンダリングできるようにすることです。

リソースの読み込みが完了した直後に LCP 要素をレンダリングできない主な理由は、なんらかの理由でレンダリングがブロックされている場合です。

  • <head> のスタイルシートまたは同期スクリプトがまだ読み込まれているため、ページ全体のレンダリングがブロックされています。
  • LCP リソースの読み込みは完了しているが、LCP 要素はまだ DOM に追加されていない(一部の JavaScript コードの読み込みを待機している)。
  • 要素が他のコード(ユーザーが参加するテストをまだ決定している A/B テスト ライブラリなど)によって非表示になっている。
  • 長時間のタスクが原因でメインスレッドがブロックされ、レンダリング処理は長時間のタスクが完了するまで待機する必要があります。

以降のセクションでは、不要な要素のレンダリング遅延の最も一般的な原因に対処する方法について説明します。

レンダリングを妨げるスタイルシートを削減またはインライン化する

HTML マークアップから読み込まれたスタイルシートは、それ以降のすべてのコンテンツのレンダリングをブロックします。これは、通常、スタイル設定されていない HTML をレンダリングしたくないため、適切な動作です。ただし、スタイルシートが非常に大きく、LCP リソースよりも読み込みに大幅に時間がかかる場合は、リソースの読み込みが完了した後でも、LCP 要素がレンダリングされなくなります。次の例をご覧ください。

大きな CSS ファイルが LCP リソースよりも読み込みに時間がかかるため、LCP 要素のレンダリングをブロックしていることを示すネットワーク ウォーターフォール図
画像とスタイルシートの読み込みは同時に開始されますが、スタイルシートの準備が整うまで画像をレンダリングすることはできません。

この問題を解決するには、次のいずれかを行います。

  • スタイルシートを HTML にインライン化して、追加のネットワーク リクエストを回避する。
  • スタイルシートのサイズを小さくします。

一般的に、スタイルシートのインライン化は、スタイルシートが小さい場合にのみおすすめします。HTML のインライン化されたコンテンツは、後続のページ読み込み時にキャッシュの効果を得ることができないためです。スタイルシートが大きすぎて読み込みに LCP リソースよりも時間がかかる場合、インライン化の有力な候補となる可能性は低くなります。

ほとんどの場合、スタイルシートが LCP 要素のレンダリングをブロックしないようにするには、LCP リソースよりも小さくなるようにサイズを小さくするのが最善の方法です。これにより、ほとんどのアクセスでボトルネックがなくなります。

スタイルシートのサイズを小さくするための推奨事項は次のとおりです。

レンダリングをブロックする JavaScript の遅延またはインライン化

ページの <head> に同期スクリプト(async 属性または defer 属性のないスクリプト)を追加する必要はほとんどありません。追加すると、ほとんどの場合パフォーマンスに悪影響を与えます。

JavaScript コードをページの読み込みのできるだけ早い段階で実行する必要がある場合は、別のネットワーク リクエストを待機してレンダリングが遅れないように、コードをインライン化することをおすすめします。ただし、スタイルシートと同様に、スクリプトが非常に小さい場合にのみインライン化してください。

すべきでないこと
<head>
  <script src="/path/to/main.js"></script>
</head>
すべきこと
<head>
  <script>
    // Inline script contents directly in the HTML.
    // IMPORTANT: only do this for very small scripts.
  </script>
</head>

サーバーサイド レンダリングを使用する

サーバーサイド レンダリング(SSR)は、サーバー上でクライアントサイド アプリケーション ロジックを実行し、完全な HTML マークアップで HTML ドキュメントのリクエストに応答するプロセスです。

LCP の最適化という観点から、SSR には主に次の 2 つの利点があります。

  • 画像リソースは HTML ソースから検出可能になります(前述のステップ 1 を参照)。
  • ページ コンテンツのレンダリングを完了するために、追加の JavaScript リクエストを待つ必要はありません。

SSR の主なデメリットは、サーバー処理時間が追加で必要になることで、TTFB が遅くなる可能性があることです。ただし、サーバーの処理時間は制御できますが、ユーザーのネットワークとデバイスの機能は制御できないため、通常はこのトレードオフは価値があります。

SSR に類似したオプションは、静的サイト生成(SSG)または事前レンダリングと呼ばれます。これは、オンデマンドではなくビルドステップで HTML ページを生成するプロセスです。アーキテクチャでプリレンダリングが可能な場合は、通常、パフォーマンスの観点からプリレンダリングを選択することをおすすめします。

長いタスクを分割する

先ほどのアドバイスに従っていても、JavaScript コードがレンダリング ブロックを行わなかったり、要素のレンダリングを担っていなかったりしても、LCP が遅れる可能性があります。

これが起こる最も一般的な理由は、ページが大きな JavaScript ファイルを読み込み、ブラウザのメインスレッドで解析して実行する必要がある場合です。つまり、画像リソースが完全にダウンロードされていても、無関係なスクリプトの実行が完了するまで待機してからレンダリングを開始しなければならない場合があります。

現在、すべてのブラウザはメインスレッドで画像をレンダリングするため、メインスレッドをブロックすると、不要な要素のレンダリング遅延が発生する可能性があります。

3. リソースの読み込み時間を短縮する

このステップの目的は、ネットワーク経由でユーザーのデバイスにリソースのバイト数を転送する時間を短縮することです。一般的には、次の 4 つの方法があります。

  • リソースのサイズを小さくする。
  • リソースの移動距離を短くする。
  • ネットワーク帯域幅の競合を軽減する。
  • ネットワーク時間を完全に排除します。

リソースのサイズを小さくする

ページに LCP リソースがある場合は、画像またはウェブフォントになります。次のガイドでは、両方のサイズを小さくする方法について詳しく説明しています。

リソースの移動距離を短くする

リソースのサイズを小さくするだけでなく、サーバーをユーザーの地理的位置にできるだけ近づけることで、読み込み時間を短縮することもできます。そのための最善の方法は、コンテンツ配信ネットワーク(CDN)を使用することです。

特に画像 CDN は、リソースの移動距離を短縮するだけでなく、通常はリソースのサイズも削減するため、前述のサイズ削減に関する推奨事項をすべて自動的に実装できるため、特に役立ちます。

ネットワーク帯域幅の競合を軽減する

リソースのサイズを小さくして移動距離を小さくしても、他の多くのリソースを同時に読み込むと、リソースの読み込みに時間がかかることがあります。この問題はネットワーク競合と呼ばれています。

LCP リソースに高い fetchpriority を指定してできるだけ早く読み込みを開始した場合、ブラウザは優先度の低いリソースが競合しないように最善を尽くします。ただし、fetchpriority が高いリソースを多数読み込む場合や、一般にリソースを大量に読み込む場合は、LCP リソースの読み込み速度に影響する可能性があります。

ネットワーク時間を完全に排除する

リソースの読み込み時間を短縮する最善の方法は、プロセスからネットワークを完全に排除することです。効率的なキャッシュ制御ポリシーでリソースを提供する場合は、リソースを 2 回目にリクエストした訪問者にキャッシュからリソースが提供されるため、リソースの読み込み時間が実質ゼロになります。

LCP リソースがウェブフォントの場合は、ウェブフォント サイズを小さくするだけでなく、ウェブフォント リソースの読み込み時にレンダリングをブロックする必要があるかどうかも検討する必要があります。font-display の値を auto または block 以外に設定すると、テキストは読み込み中に常に表示されます。また、追加のネットワーク リクエストで LCP がブロックされることはありません。

最後に、LCP リソースが小さい場合は、リソースをデータ URL としてインライン化することをおすすめします。これにより、追加のネットワーク リクエストも不要になります。ただし、データ URL を使用する場合、リソースをキャッシュに保存できず、デコードコストが増加してレンダリングの遅延が長くなる場合があるため、注意点があります。

4. 最初のバイトまでの時間を短縮する

このステップの目的は、初期 HTML をできるだけ早く提供することです。このステップはデベロッパーが最も管理できないことが多いため、最後に記載します。ただし、このステップはその後のすべてのステップに直接影響するため、最も重要なステップの一つでもあります。バックエンドがコンテンツの最初のバイトを配信するまで、フロントエンドで何も起こりません。そのため、TTFB を高速化するためにできることはすべて、他のすべての読み込み指標の改善にもつながります。

サイトの速度は速いのに TTFB が遅い場合、一般的な原因として、広告や短縮リンクなど、複数のリダイレクトを経由してユーザーがアクセスしていることがあります。訪問者が待機しなければならないリダイレクトの数は常に最小限に抑えます。

もう一つの一般的な原因は、キャッシュに保存されたコンテンツを CDN エッジサーバーから使用できず、すべてのリクエストを配信元サーバーに送信する必要があることです。これは、訪問者がアナリティクス用に一意の URL パラメータを使用している場合に発生することがあります(ページが異なる場合でも発生することがあります)。

TTFB の最適化に関する具体的なガイダンスについては、TTFB の最適化ガイドをご覧ください。

JavaScript で LCP の内訳をモニタリングする

前述のすべての LCP サブパートのタイミング情報は、次のパフォーマンス API を組み合わせて JavaScript で表示できます。

これらのタイミング値を JavaScript で計算するメリットは、分析プロバイダに送信したり、デベロッパー ツールにログに記録したりして、デバッグと最適化に役立てられることです。

たとえば、次のスクリーンショットは、User Timing APIperformance.measure() メソッドを使用して、Chrome DevTools の [Performance] パネルの [Timings] トラックにバーを追加しています。

Chrome DevTools で可視化された LCP サブカテゴリの User Timing の測定値
タイミング トラックには、LCP サブカテゴリのタイムラインが表示されます。

[タイミング] トラックの可視化は、[ネットワーク] トラックと [メインスレッド] トラックとともに確認すると特に役立ちます。これらの期間中にページ上で他に何が起こっているかを一目で確認できます。

タイミング トラックで LCP サブパートを可視化するだけでなく、JavaScript を使用して、LCP の合計時間に対する各サブパートの割合を計算することもできます。この情報に基づいて、前述の推奨される割合の分類をページが満たしているかどうかを判断できます。

このスクリーンショットは、各 LCP サブパートの合計時間と、LCP の合計時間に対するその割合をコンソールにロギングする例を示しています。

LCP サブカテゴリの時間と、LCP の割合がコンソールに出力される
LCP サブカテゴリのタイミングと割合。

これらの可視化はどちらも次のコードを使用して作成されました。

const LCP_SUB_PARTS = [
  'Time to first byte',
  'Resource load delay',
  'Resource load duration',
  'Element render delay',
];

new PerformanceObserver((list) => {
  const lcpEntry = list.getEntries().at(-1);
  const navEntry = performance.getEntriesByType('navigation')[0];
  const lcpResEntry = performance
    .getEntriesByType('resource')
    .filter((e) => e.name === lcpEntry.url)[0];

  // Ignore LCP entries that aren't images to reduce DevTools noise.
  // Comment this line out if you want to include text entries.
  if (!lcpEntry.url) return;

  // Compute the start and end times of each LCP sub-part.
  // WARNING! If your LCP resource is loaded cross-origin, make sure to add
  // the `Timing-Allow-Origin` (TAO) header to get the most accurate results.
  const ttfb = navEntry.responseStart;
  const lcpRequestStart = Math.max(
    ttfb,
    // Prefer `requestStart` (if TOA is set), otherwise use `startTime`.
    lcpResEntry ? lcpResEntry.requestStart || lcpResEntry.startTime : 0
  );
  const lcpResponseEnd = Math.max(
    lcpRequestStart,
    lcpResEntry ? lcpResEntry.responseEnd : 0
  );
  const lcpRenderTime = Math.max(
    lcpResponseEnd,
    // Use LCP startTime (the final LCP time) because there are sometimes
    // slight differences between loadTime/renderTime and startTime
    // due to rounding precision.
    lcpEntry ? lcpEntry.startTime : 0
  );

  // Clear previous measures before making new ones.
  // Note: due to a bug, this doesn't work in Chrome DevTools.
  LCP_SUB_PARTS.forEach((part) => performance.clearMeasures(part));

  // Create measures for each LCP sub-part for easier
  // visualization in the Chrome DevTools Performance panel.
  const lcpSubPartMeasures = [
    performance.measure(LCP_SUB_PARTS[0], {
      start: 0,
      end: ttfb,
    }),
    performance.measure(LCP_SUB_PARTS[1], {
      start: ttfb,
      end: lcpRequestStart,
    }),
    performance.measure(LCP_SUB_PARTS[2], {
      start: lcpRequestStart,
      end: lcpResponseEnd,
    }),
    performance.measure(LCP_SUB_PARTS[3], {
      start: lcpResponseEnd,
      end: lcpRenderTime,
    }),
  ];

  // Log helpful debug information to the console.
  console.log('LCP value: ', lcpRenderTime);
  console.log('LCP element: ', lcpEntry.element, lcpEntry.url);
  console.table(
    lcpSubPartMeasures.map((measure) => ({
      'LCP sub-part': measure.name,
      'Time (ms)': measure.duration,
      '% of LCP': `${
        Math.round((1000 * measure.duration) / lcpRenderTime) / 10
      }%`,
    }))
  );
}).observe({type: 'largest-contentful-paint', buffered: true});

このコードは、ローカル デバッグにそのまま使用できます。また、このデータをアナリティクス プロバイダに送信するように変更して、実際のユーザーのページで LCP がどのように分類されているかを詳しく把握することもできます。

概要

LCP は複雑な指標であり、そのタイミングはさまざまな要因によって影響を受ける可能性があります。ただし、LCP の最適化は主に LCP リソースの負荷の最適化に関するものだと考えると、状況を大幅に簡素化できます。

LCP の最適化は、大まかに次の 4 つのステップに分けられます。

  1. LCP リソースの読み込みができる限り早く開始されるようにします。
  2. LCP 要素がリソースの読み込みが完了するとすぐにレンダリングされるようにします。
  3. 品質を損なうことなく、LCP リソースの読み込み時間をできるだけ短縮します。
  4. 最初の HTML ドキュメントをできるだけ早く配信してください。

これらの手順をページ上で実践できれば、ユーザーに最適な読み込みエクスペリエンスを提供できていると確信でき、実際の LCP スコアにも反映されているはずです。