Largest Contentful Paint を最適化する

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

公開日: 2020 年 4 月 30 日

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

優れたユーザー エクスペリエンスを提供するには、ページ訪問の 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 の [パフォーマンス] パネルのローカル 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 属性を非表示にする JavaScript ライブラリによって遅延読み込みされます(多くの場合、data-src または data-srcset として)。
  • 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 の [パフォーマンス] パネルのタイミング トラックにバーを追加しています。

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 スコアに反映されます。