ウェブサイトのINPを改善する方法:Core Web Vitalsの最適化でSEOを強化



INP(Interaction to Next Paint)は、GoogleのCore Web Vitals(CWV)(コアウェブバイタル)の重要な指標の一つで、ユーザーのインタラクション(クリック、タップ、キーボード入力など)に対するページの応答性を測定します。
INPが悪いとユーザー体験が低下し、SEOにも悪影響を及ぼします。

この記事では、どんなウェブサイトでも使えるINPの改善策を、具体的なコード例とともにステップごとに解説します。
目標は、INPを200ms以下に抑えて検索ランキングを向上させることです。

コアウェブバイタルの影響に関してはこちらをご覧ください。

画像の圧縮に関してはこちら

INPとは? なぜ改善が必要か

INPは、ページ訪問中の全インタラクションのうち最も遅かったものを代表値として評価します(通常、75パーセンタイル値)。
公式の目安は以下の通りです。

  • ≤200ms:良好

  • 200–500ms:要改善

  • >500ms:不良

2024年3月12日に、従来のFID(First Input Delay)からINPへ正式移行しました。
Google Search Console(GSC)のCWVレポートもINP基準です。1回の遅延が全体の評価を下げるため、INPの最適化はSEOにおいて必須です。

主な原因は、重いJavaScript(JS)やiframeによる埋め込みコンテンツ(YouTube、X/Twitter、Instagram、地図など)です。これらがクリックやメニュー開閉などの応答を阻害し、INPを悪化させます。

INP悪化の主な原因:埋め込みコンテンツと重いJS

YouTube動画、ソーシャルメディアのポスト、地図、スライドなどの埋め込みは、重いJSやiframeを伴い、ページの応答性を下げる傾向があります。
改善の3本柱は以下の通りです。

  1. 読まれるまで読み込まない:遅延読み込みやファサード(擬似表示)で初期負荷を軽減。

  2. 読み込むなら軽く:async/deferやタスク分割で高速化。

  3. 不要なものは外す:第三者JSの棚卸しで無駄を排除。

以下、具体的な実装方法を解説します。
実装方法を聞いても難しいという方は専門家への相談、もしくは当サイトにコメント、お問い合わせいただければと思います。

実装の準備:コードの設置場所

  • 共通スクリプト/CSS<head>内に配置(全ページ適用)。

  • 個別コンテンツ:埋め込みは記事本文や該当ページに直接記述。

注意:CMS(WordPress、Wixなど)使用時は、テーマのヘッダー設定やプラグインを活用してください。

A. 遅延読み込みをデフォルトに

ブラウザ標準のloading="lazy"を使い、画面外のiframeや画像を遅延読み込み。
これだけでもINPが改善します。

A-1. iframeと画像にloading="lazy"を適用

YouTube埋め込みの例

<iframe
  src="https://www.youtube.com/embed/xxxxxxx"
  title="動画タイトル"
  width="560" height="315"
  loading="lazy"  <!-- 画面外の遅延読み込み -->
  referrerpolicy="strict-origin-when-cross-origin"
  allowfullscreen></iframe>

画像の例

<img src="example.jpg" alt="説明" width="800" height="450" loading="lazy">

ポイント

  • 全ての埋め込みと画像にloading="lazy"を付与。

  • widthheightを指定してCLS(Cumulative Layout Shift)を抑制。

A-2. ファサードでさらに効果を最大化

ファサードは、初期表示を静的なサムネイルやプレースホルダーにし、クリック時にのみ本物のコンテンツをロードする方法。
大量の埋め込みがある場合に特に有効です。

YouTube用ファサード(バニラJS実装)

<head>に追加(共通CSS/JS):

<style>
.yt-facade {
  position: relative;
  inline-size: 100%;
  aspect-ratio: 16/9;
  background: #000 center/cover no-repeat;
  border-radius: .75rem;
  box-shadow: 0 2px 12px rgba(0,0,0,.12);
  cursor: pointer;
}
.yt-facade::after {
  content: "▶";
  position: absolute;
  inset: auto 0 0 0;
  margin: auto;
  inline-size: 64px;
  block-size: 64px;
  display: grid;
  place-items: center;
  background: rgba(255,255,255,.92);
  border-radius: 999px;
  transform: translateY(-10%);
  font: 700 28px/1 system-ui;
  color: #000;
}
.yt-facade.loading::after {
  content: "…";
}
</style>
<script>
document.addEventListener('DOMContentLoaded', () => {
  const els = document.querySelectorAll('.yt-facade[data-ytid]');
  els.forEach(el => {
    const id = el.dataset.ytid;
    el.style.backgroundImage = `url(https://i.ytimg.com/vi/${id}/mqdefault.jpg)`;
    el.addEventListener('click', () => {
      if (el.dataset.loaded) return;
      el.classList.add('loading');
      const iframe = document.createElement('iframe');
      iframe.width = '560';
      iframe.height = '315';
      iframe.loading = 'eager';
      iframe.src = `https://www.youtube.com/embed/${id}?autoplay=1`;
      iframe.title = el.dataset.title || 'YouTube video';
      iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
      iframe.referrerPolicy = 'strict-origin-when-cross-origin';
      iframe.allowFullscreen = true;
      el.replaceWith(iframe);
    }, { once: true });
  });
});
</script>

本文に貼る(動画ごと):

<div class="yt-facade" data-ytid="VIDEO_ID" data-title="タイトル"></div>

代替:より洗練された実装には、GitHubのlite-youtube-embedライブラリを検討。

 

X(Twitter)/Instagramのファサード

本文に貼る:

<div class="tweet-facade" data-url="https://x.com/USER/status/1234567890">ツイートを見る</div>

<head>に追加(共通JS):

<script>
document.addEventListener('click', e => {
  const t = e.target.closest('.tweet-facade');
  if (!t) return;
  if (!window.twttr) {
    const s = document.createElement('script');
    s.src = 'https://platform.twitter.com/widgets.js';
    s.async = true;
    s.onload = () => render();
    document.head.appendChild(s);
  } else {
    render();
  }
  function render() {
    const url = t.dataset.url;
    const bq = document.createElement('blockquote');
    bq.className = 'twitter-tweet';
    bq.innerHTML = `<a href="${url}"></a>`;
    t.replaceWith(bq);
    if (window.twttr) window.twttr.widgets.load();
  }
});
</script>

 

Googleマップの代替

重いiframeの代わりに、静的リンク+サムネイル画像を使用。
必要ならファサード化してください。

<a href="https://maps.google.com/?q=住所" target="_blank">
  <img src="static-map.jpg" alt="地図" width="600" height="400" loading="lazy">
</a>

B. 読み込むなら「軽く」:第三者JSの最適化

  • async/defer<script async>または<script defer>でHTML解析のブロッキングを回避。

    <script defer src="example.js"></script>
  • preconnect:CDN接続を高速化(多用注意)。

    <link rel="preconnect" href="https://example-cdn.com">
  • 長いタスクの分割:巨大JSや同期処理を細かく分割。requestIdleCallbacksetTimeoutで処理を分散。

  • 不要JSの削除:未使用の計測タグやシェアボタンを棚卸し。

C. ウェブサイト全般での注意点

  • コードハイライト:必要なページだけCDNから読み込む(<head>常駐は避ける)。

  • 埋め込みの制限:1画面に1つまでを目安。残りはファサード化。

  • 画像最適化:適切なサイズ、フォーマット(WebP推奨)、loading="lazy"、width/height指定。

  • JS/CSSの軽量化:不要な装飾やアニメーションを削減。

INPの測定と診断ワークフロー

毎ページチェックを習慣化して診断すると効果的です。

  1. GSCで確認:コアウェブバイタル>INP(モバイル、28日移動平均、75%タイル)。

  2. PageSpeed Insights:URLを入力し、フィールドINPとラボ診断を確認。

  3. DevTools(Performanceパネル):遅い操作(例:メニュー開閉)を再現し、Long Taskを特定。

  4. サードパーティ負荷の特定:LighthouseやDevToolsで重いスクリプトを洗い出し。

対処の優先順位

  1. 削除:不要なコンテンツや価値の低い埋め込みやタグは削除してしまうのが吉です。

  2. ファサード化:YouTube、X、地図などを遅延読み込み。

  3. 遅延+async/defer:必要なスクリプトを最適化。

  4. タスク分割:長い処理を細分化。

目標と効果

目標:INP ≤200ms(モバイル、75%タイル)。
1ページずつ改善することで、ユーザー体験が向上し、SEO効果も見込めます。実装後はGSCやPageSpeed Insightsで変化を監視しましょう。

これらの施策で、どんなウェブサイトでもINPを最適化し、検索上位を目指せます。早速実装を始めて、効果を実感してください!

専門的な改善内容のため、ご自身での対応が難しい方は専門家に相談するようにしてください。