上級者向け

第3回: 「重い」を許さない。Core Web VitalsとINPの外科手術

第3回「重い」を許さない。Core Web VitalsとINPの外科手術
この記事の目次 読みたい項目へ移動
WEBさん
WEBさん

「重い」には必ず計測可能な原因があります。INP の数字を恐れずに、原因を 1 つずつ取り出していけば、必ず速くなります。

📌 この記事の要点

  • 2024 年以降の UX 指標 INP が表示速度の評価を変えた
  • JavaScript の Long Tasks と画像最適化(AVIF/WebP)でボトルネックを外科手術的に除去
  • Object Cache が逆に遅延を生む「キャッシュの毒」パターンの見抜き方

2024年以降、GoogleのUX評価はCore Web Vitals(コアウェブバイタル)という
LCP / CLS / INP の3指標を中心に設計されています。

PageSpeed Insights のスコアは悪くない。
それでも、

  • クリックすると一瞬止まる
  • 入力やスクロールに引っかかりを感じる

この違和感を放置しているサイトは、2024 年以降、検索評価指標として継続的に扱われます。

その正体が INP(Interaction to Next Paint) です。

本記事では、

  • なぜ「表示は速いのに重く感じる」のか
  • 何がINPを悪化させているのか
  • どこを見て、どこを切ればよいのか

を、現場で使えるレベルまで分解します。

INPとは何か。何を測り、何を測らないのか

INP(Interaction to Next Paint)の定義

重要なのは、INPは次のものを直接は測っていないという点です。

  • 通信速度
  • 初回表示速度
  • サーバーレスポンス単体

つまり、

  • 読み込み速度は良い
  • ページはすぐ表示される

それでも、

  • ボタンを押すと一瞬止まる
  • 入力時に引っかかる

この「操作中の詰まり」を数値化するのが INP です。

📊 INP の評価基準(目安)

評価INP(応答遅延)
Good200ms 以下
Needs Improvement200〜500ms
Poor500ms 超

なぜ PageSpeed Insights だけでは足りないのか

PageSpeed Insights(PSI)は有用なツールです。しかし INP改善の主戦場ではありません。

理由は以下です。

  • PageSpeed Insightsはスコア(合否)を知るには適しているが、『どの操作が遅いのか』という原因特定には向いていない
  • 静的な読み込みシナリオが主体
  • ユーザー操作中の詰まりを再現しにくい

INPはユーザーが操作した「その瞬間」に何が起きているかを問題にします。

そのため、使うべきツールは Chrome DevTools です。

Chrome DevTools:INP改善で見るべきポイント

Performance タブをクリックすると LCP・CLS・INPを確認できます。

Performanceタブとは何か

INP改善では、特に以下を見ます。

  • Experience
  • Main Thread
  • Long Tasks

Main Threadとは何か

ここが占有されると、

  • 入力を受け付けない
  • 描画が遅れる

結果として INPが悪化 します。

Long Taskとは何か

Long Taskを識別するための閾値です。人が必ず遅れを感じ始める一律の境界や、INPの合否基準を意味しません。

INP が悪いサイトでは、多くの場合この Long Task が背景にあります。

JavaScript実行遅延と「犯人プラグイン」の特定

なぜJavaScriptがINPを悪化させるのか?

ユーザー操作は、次の流れで処理されます。

  1. 入力イベント発生
  2. JavaScriptイベントハンドラ実行
  3. DOM更新
  4. 描画

という流れで処理されます。

この途中で、

  • 重いJavaScript
  • 同期処理
  • 不要な初期化

が挟まると、描画が遅れ、INPが悪化します。

特定手順

  1. Chrome DevTools → Performanceタブ
  2. 実際に「重い操作」を行う
  3. 記録を停止する
  4. Main Thread を確認
  5. 50ms超の Long Task を探す
  6. Call Tree / Bottom-Up を確認

用語補足

  • Call Tree:処理の呼び出し関係
  • Bottom-Up:処理時間の消費順

重要なのは「どこから始まったか」ではなく、
「何が時間を消費しているか」です。

テーマ・プラグインのJSを切り分ける

  • テーマJS:ページ単位で制御されやすい
  • プラグインJS:全ページ常駐しやすい

特に以下は要注意です。

  • DOMContentLoaded 後に重い初期化を行うJS
  • ユーザー操作をフックして同期処理を行うJS
  • 管理画面とフロントで共用されているJS

これらの処理がクリック・タップ・キー入力から次の描画までを妨げる場合、INPが悪化することがあります。処理の実行時期と時間をPerformance記録で確認して判断します。

画像最適化の最終解とlazy-loadの落とし穴

AVIF / WebP の実務的な使い分け

現時点での現実解は以下です。

  • AVIF:対応ブラウザ向け
  • WebP:フォールバック
  • JPEG:最終保険

picture 要素での出し分けが基本です。

LCP画像の遅延とINPへの影響を分ける

loading="lazy" は万能ではありません。

lazy-load は
表示を遅らせる技術であって、速くする技術ではありません。

問題が起きやすいケース

  • ファーストビュー内の画像
  • LCP候補画像
  • ヒーロー画像

これらをlazyにすると取得開始や表示が遅れ、LCPを悪化させる場合があります。INPは画像の通信待ち時間そのものではないため、次を分けて確認します。

  1. LCP画像の取得・表示が遅れていないか
  2. クリック・タップ・キー入力と重い処理が重なっていないか
  3. 画像に関わる処理や描画が、操作への次の描画を妨げていないか
  4. DevToolsで対象操作のメインスレッドと次の描画を確認する

操作時の処理や次の描画を妨げる場合に、INPへ影響し得ます。遅延読み込みを使っただけで悪化すると決めず、INPの測定範囲に沿ってトレースを確認してください。

原則:LCPには「優先タグ」を、それ以外には「遅延」を

  • ファーストビュー(LCP画像):
    loading="lazy" は使用しない。さらに、モダンブラウザ向けに fetchpriority="high" 属性を付与することを推奨します。これにより、ブラウザに対して「これは最優先で取得すべきリソースだ」と明示でき、描画開始を早めることができます。
  • 下層画像(ファーストビュー外):
    loading="lazy" を使用して、初期ロードの阻害を防ぎます。

Object Cache(Redis / Memcached)が「毒」になる瞬間

Object Cache は強力ですが、条件次第では逆に遅延要因になります。

なぜキャッシュなのに遅くなるのか

Object Cacheは、

  • PHP → Redis → PHP

という往復処理を増やします。

Object Cache は魔法ではありません。導入することで以下のコストが新たに発生します。

  1. ネットワークレイテンシ:
    WebサーバーからRedisサーバーへの通信時間
  2. シリアライズ/デシリアライズ:
    データを保存・復元するための変換処理コスト

キャッシュヒット率が低い場合、またはキャッシュするデータ自体が巨大な場合、「単純にPHPで再計算する時間」よりも「Redisからデータを取ってくる時間」の方が長くなるという逆転現象(オーバーヘッド)が発生します。

これはサーバーの応答時間が伸びる原因候補です。Redis待機時間をそのままINPとみなすことはできません。INPは操作から次の描画までを測るため、ブラウザ側の入力遅延・イベント処理・描画遅延を別に記録します。

サーバー待機とINPを分けて確認

  • サーバー側のキャッシュ処理を計測
  • Redis応答待ち
  • ブラウザの処理・描画を別に計測

非同期リクエストの応答待ちだけなら、ブラウザは待機表示などを先に描画できます。一方、応答後の重いJavaScriptが別の操作や描画を妨げる場合には、INPへ影響する可能性があります。トレースで両者を区別してください。

対策の考え方

  • すべてを無条件にキャッシュせず、対象・キー・有効期限を選ぶ
  • 利用者固有の情報を共用キャッシュから除外
  • Query Monitorでヒット状況を確認

Query Monitor再考:見るべきはHTTP待機時間

Query Monitor=スロークエリ
という理解は不十分です。

現代のWordPressは、

  • REST API
  • 外部API
  • ブラウザ側のfetchはNetworkパネルで別に調べる

などの通信を使うためです。PHPからのHTTP API呼出しはQuery Monitorで、ブラウザ側の通信はDevToolsのNetworkで確認します。

なぜHTTP API Callsを見るべきか

Query Monitor の HTTP API Calls では、

  • 外部リクエストの待機時間
  • ブロッキングの有無

を確認できます。これはPHP側のHTTP API呼び出しであり、ブラウザのfetchとは別です。サーバーの待機時間と、操作から次の描画までのINPを混同せず、必要に応じて両側のログ・トレースを照合します。

上級者Q&A

Q1. ここでいう「動的ブロック」とは何ですか?

ここでは、サーバーで描画時にHTMLを生成するブロックを指します。動的レンダリングだから必ず利用者ごとに異なる、毎回外部APIを呼ぶ、あるいはキャッシュできないという意味ではありません。

具体的には、

  • ユーザーごとに内容が変わる
  • 時間・状態・権限によって表示が変わる
  • PHP / REST API / 外部API を毎回呼び出す

といった特性を持つブロックを指します。

Q2. 動的ブロックをCDNでキャッシュするときの注意点は?

利用者ごとの内容や有効期限の違いを無視してHTMLを共用すると、情報が混ざったり古い内容を返したりするためです。サーバーで動的に生成したHTMLでも、利用者共通の内容なら、有効期限・無効化・キャッシュキーを設計したうえでキャッシュできます。

利用者固有の内容を区別せず共有キャッシュに保存すると、以下の事故が起きるおそれがあります。

  • 他人の情報が表示される
  • ログイン状態が混線する
  • 管理者向け情報が一般ユーザーに見える
  • 表示条件が壊れる

つまり、
速度と引き換えに安全性を失う状態になります。

利用者固有の内容は共用キャッシュから除外し、共通部分だけをキャッシュするなど、内容と認可に合わせて対象を決めます。動的生成という理由だけで一律に除外する必要はありません。

Q3. では、動的ブロックは一切キャッシュできないのですか?

いいえ。全利用者に同じ内容を返すブロックなら、HTML全体をキャッシュできる場合もあります。個別情報を含む場合は、部分を分けるか適切にキャッシュを回避します。

ポイントは、
静的部分と動的部分を分離することです。

Q4. 「切り離す」とは、具体的にどういう意味ですか?

役割ごとに処理を分ける、という意味です。

部分処理方法
ページ全体のHTMLCDN / ページキャッシュ
固定文言・レイアウト静的HTMLとして配信
ユーザー依存・状態依存Ajax / REST API で後読み

これにより、

  • ページは高速に表示される
  • 動的情報は必要な分だけ取得される
  • API側も認可とキャッシュ制御を明示する

という状態を作れます。

Q5. Ajax / REST APIに分ければ安全ですか?

分離だけでは安全になりません。APIにもキャッシュは適用され得ます。利用者ごとの認証・認可を行い、機密情報を保存させない場合はCache-Controlなどの応答ヘッダーとCDN設定を合わせて確認します。

Ajax / REST API は、

  • 内容に応じてキャッシュを許可または抑止する
  • ユーザー単位で処理できる
  • 権限チェックを個別に行える

という特性があります。

つまり、

  • 表示速度は CDN に任せる
  • 個別処理は API に任せる

という分担にできます。ただし、APIに移すだけで認可やキャッシュ分離が自動的に保証されるわけではありません。

Q6. Edge Cache や ESI(Edge Side Includes)で解決できませんか?

ケースによりますが、過信は危険です。

理由は、

  • ESI対応CDNは限られる
  • 実装が複雑になりやすい
  • WordPress側の責務が不透明になる

ためです。

実務では、

「まずは静と動を分ける」
それ以上の最適化は必要になってから

が安全な判断です。

Q7. INP(体感速度)との関係は?

分離によってブラウザのメインスレッドの負荷を減らせる場合、INP改善につながる可能性があります。非同期化しただけでは保証されず、応答後の処理や再描画も含めて計測します。

理由は、

  • 不要な処理を操作時にまとめて実行しないよう調整できる
  • Main Threadの長い処理を短く分割できる場合がある
  • ユーザー操作と描画が競合しにくい

からです。

INPが悪いサイトでは、
「操作直後に動的処理が走っている」 ケースが非常に多く見られます。

Q8. 結局、どう判断すればいいですか?

次の質問で、共有できる内容と利用者固有の内容、処理負荷を分けます。必要に応じて部分キャッシュやAPIへの分離を検討します。

  • ユーザーごとに内容が変わるか
  • 毎回PHP処理やAPI通信が走るか
  • キャッシュすると事故が起きそうか
  • 操作直後に処理が走っていないか

利用者固有の情報や認可の扱いが確認できない部分は、共用キャッシュから外して調査します。PHP処理やAPI通信があることだけで、すべてをキャッシュ不可とは判断しません。

今すぐできる INP改善チェックリスト

最後に、本記事の内容を自サイトに適用するためのチェックリストをまとめました。まずはこれらを潰すだけで、改善の糸口が見えるはずです。

  • LCP画像の確認:
    ファーストビュー画像の loading="lazy" を削除し、fetchpriority="high" を記述したか?
  • Long Taskの特定:
    Chrome DevToolsで操作中に「50ms以上」の赤いバーが出ているか確認したか?
  • プラグインの選別:
    全ページで読み込まれている不要なプラグインJavaScriptを停止したか?
  • 動的ブロックの隔離:
    ログイン状態やユーザー情報を含むブロックを、HTMLキャッシュから除外(またはJS化)したか?

よくある質問(FAQ)

Q. PageSpeed Insights のスコアが良いのに体感が遅いのはなぜですか。
A. PageSpeed Insights のラボデータは特定条件での測定値です。INP は実際の操作への応答を測るため、管理画面にログインした状態や低速端末では結果が変わります。実測値(フィールドデータ)を併せて確認してください。
Q. INP を悪くしている犯人プラグインはどう特定しますか。
A. Chrome DevTools の Performance タブで操作を記録し、Long Task のスタックを見ます。実行元のスクリプトURLに含まれるプラグイン名が手がかりになります。1つずつ止めて確認する方法もありますが、一括では原因が分かりません。
Q. lazy-load を全画像に付ければ速くなりますか。
A. ファーストビューの画像に付けると逆効果です。最初に見える画像は eager にして fetchpriority=”high” を指定し、それ以外を lazy にするのが基本です。
Q. Redis を入れたのに遅くなりました。
A. オブジェクトキャッシュは、キャッシュの取得コストが再計算コストを上回ると逆効果になります。巨大なトランジェントを大量に保存している場合や、ネットワーク越しの接続が遅い場合に起こります。

まとめ

第1回では「何が起きているか」をログから特定しました。
第2回では「構造としてどこに無理があるか」を設計視点で整理しました。

そして第3回では、
ユーザー操作で何が詰まっているのかをINPを軸に可視化してきました。

ここで見えてきたのは、
パフォーマンス問題の多くがJavaScriptとレンダリングの境界で起きているという事実です。

以下に、本記事の要点を箇条書きでまとめておきます。

  • INPは「操作中の詰まり」を暴く指標
  • 点数より DevTools
  • JS Long Task が最大の敵
  • キャッシュも使い方次第で毒
  • 計測 → 切除 → 再計測

次回、第4回では

「動かない」の深層。React / Headless / JSエラーの外科解剖
に進みます。

WEBさん
WEBさん

数値は見たけど、どこから手をつければ最も効果が出るか分からない場合は、実機で測りながら優先順位を整理した方が結果が早く出ます。

コメントを残す

メールアドレスは公開されません。* は必須項目です。

CAPTCHA