「サーバーを強いプランに変えれば速くなりますか?」——表示速度の相談で、最初にこう聞かれることがよくあります。気持ちはわかります。遅い=サーバーが非力、という筋書きはわかりやすい。

ただ、自分のサイトで同じ疑いを持って計測したとき、答えは違いました。TTFB(HTMLの最初の1バイトが返るまで)は約10ミリ秒。応答は充分に速い。それでもLCPは4.8秒ありました。犯人はサーバーの外にいたわけです。

LCPは「一番大きい要素が描き終わるまでの時間」なので、遅くなる理由は一つではありません。内訳を4つに割ると、どこを直せばいいかが決まります。この記事は、既存サイトを作り直さずLCPだけ手当てする前提で書きます。指標そのものの基礎は表示速度とCore Web Vitalsの記事にまとめました。

LCPは4つの区間に分けて読む

LCP(Largest Contentful Paint)は、画面に見えている範囲で一番大きい画像やテキストが描画されるまでの時間です。良好の目安は、実利用者の75パーセンタイルで2.5秒以下。

この時間は、次の4区間に分解できます。

LCPを4区間に分ける。1 TTFBはHTMLの最初の1バイトが返るまでで、サーバーの速さが出るのはここだけ。2 リソース読み込みの待ち時間は画像を取りに行き始めるまでの空白で、発見の遅さ。3 リソースの読み込み時間は画像を落とし切るまで。4 描画までの遅れは読み込み後に画面へ出るまでで、演出で遅れるのはここ
  • TTFB:HTMLの最初の1バイトが返るまで。サーバーの速さが出るのは、ここだけです
  • リソース読み込みの待ち時間:TTFBのあと、LCPの画像を取りに行き始めるまでの空白。ダウンロード時間ではなく「見つけるのが遅い」時間
  • リソースの読み込み時間:その画像を落とし切るまで
  • 描画までの遅れ:読み込みが終わってから、実際に画面へ出るまで

LCP要素が画像ではなくテキストなら、真ん中の2つはゼロになります。

どの区間が太っているかで、打ち手はまるで変わります。サーバー増強が効くのは1番目だけ。私のトップページは、その1番目がほぼゼロでした。しかもLCP要素はヒーローの見出しテキスト。つまり2番目と3番目もゼロで、4.8秒はまるごと4番目、描画までの遅れだったわけです。ここを見ずにサーバーを変えていたら、費用をかけて何も変わらなかったはずです。

画像は「重い」のではなく「大きすぎるものを選んでいた」

先に2番目と3番目、画像の話をします。カードや写真が主役の画面では、LCP要素が画像になります。そのとき効いてくるのがここです。

実績カードの並ぶページで、画像を合計1.9MBほど落としていました。ここで犯人にされがちなのは圧縮です。でも実際は、画像はWebPで最適化済み、配信もエッジから出ていて速い。おかしいのは、ブラウザに渡していた sizes の指定でした。

srcsetで複数の幅を用意しても、どの幅を使うかを決めるのはブラウザです。その判断材料が sizes。この指定がレイアウトの実寸より大きいと、ブラウザは必要のない大きな候補を、律儀にダウンロードします。2カラムで実寸550pxほどのカードに、1200px幅の画像を落としていました。指定だけが間違っていたんです。

カードの実寸に合わせて書き直したら、合計は0.6MB前後まで落ちました。

sizes既定のままと実寸に合わせたsizesの比較。既定はブラウザが1200w候補をダウンロードしページ合計約1.9MB、実寸を明示すると640w候補で約0.6MB。画像はどちらもWebP最適化済みでエッジ配信
<!-- レイアウトの実寸に合わせる。コンポーネントの既定値のままにしない -->
<Image
  image={featuredImage}
  sizes="(min-width: 1280px) 600px, (min-width: 900px) 46vw, 92vw"
/>

画像コンポーネントの既定の sizes は、たいてい「一番大きいレイアウト」を想定した値です。ライブラリの責任ではなく、実寸を知っているのは実装側だけ、という話。カードやサムネイルに使うときは必ず上書きする、と決めておくと事故が減ります。

ファーストビューの主役画像なら、遅延読み込みを外して優先度を上げるのも効きます。私が使っているEmDashのImageコンポーネントは priority を付けると loading="eager"fetchpriority="high" が付く仕様です。ヒーロー画像のような「最初に必ず要るもの」だけに使います。逆に、LCP要素に loading="lazy" が付いていると発見が遅れます。2番目の区間が太いときは、まずここを疑います。

演出が描画を遅らせていた

残る犯人が4番目、描画までの遅れでした。これが一番やっかいで、コードの書き方の問題ではありません。

私のトップページのヒーローは、チャットのやり取りが順に現れる演出です。一番大きいテキストが出揃うのは、数秒後。ブラウザから見れば「最大要素が描き終わったのは4秒後」で、そのままLCPになります。演出の尺が、そっくり指標の数字に化けていたわけです。

ここはデザインと実装の境目なので、実装者だけでは決められません。とはいえ、演出を捨てる必要もありませんでした。私がやったのは2つです。

  • 初期フレームをHTMLで描く:JSが動く前でも、ヒアリング画面が見えている状態をサーバー側レンダリングで出す。真っ白から始めない
  • 冒頭の尺を詰める:物語の構成は変えず、最初の要素が出揃うまでを短く圧縮する
演出の尺とLCPの比較。尺をそのままにすると初期表示はJSが動くまで空白で、最大テキストは数秒かけて出揃い、LCPは演出の終わりの4.8秒で確定する。初期フレームをHTMLで即描画し冒頭を圧縮すると、演出の構成は変えずに冒頭でLCPが確定する

動きは残しつつ、最大要素だけ早く確定させる。この折衷はたいてい成立します。デザイン側と話すときは、こう整理すると通りやすいと感じています。

  • ファーストビューの見出しと主画像は、フェードインの対象から外すか尺を詰める
  • 凝った演出は、ファーストビューの外(スクロールした先)に置く。そこはLCPに影響しない
  • 「動き出すまで何も見えない」設計にしない

スクロール演出そのものを重くしない設計はGSAPの記事で扱っています。

計測してから、太いところだけ削る

手順としては、内訳を出す、太い区間を特定する、そこだけ直す。この順番を守るだけで、無駄な工事がかなり減ります。

私の場合、効いたのは sizes の1行と、演出の尺の調整でした。サーバーには一切触っていません。「遅い」の相談が来たとき、作り直しやプラン変更を提案する前に、まず内訳を見る価値はここにあります。触ったときの反応が鈍い場合は指標が別で、そちらはINPだけ直す記事にまとめました。

既存サイトが遅い、でも作り直す予算はない。原因がサーバーなのかフロントなのかも切り分けられていない。そういうときは、計測して削れるところから直す方法があります。現状のどこが重いのかを見るところからお手伝いしますので、お問い合わせからお気軽にどうぞ。

本記事は2026年7月時点の情報です。Core Web Vitalsの指標仕様や判定基準は変わる可能性があるため、実装の際は最新の公式情報もあわせてご確認ください。