Article

モバイル SEO 対策用語集|専門用語をやさしく解説

高田晃太郎
モバイル SEO 対策用語集|専門用語をやさしく解説

モバイル経由の検索はすでに過半を占め、Googleはモバイル版コンテンツを主軸にインデックスする¹。さらにCore Web Vitalsはランキング要素に組み込まれ³、フィールド実測のLCP・CLS・INPがビジネス成果を左右する²。にもかかわらず、用語の定義と実装の距離が遠いチームは多い。本稿はCTO・技術リーダー向けに、モバイルSEOの主要用語を技術仕様とコードで接続し、計測とROIまで一気通貫で整理する。

用語集と設計指針:定義を実装に落とす

重要用語の定義だけでなく、実装上の要点と計測観点を併記する。設計初期にこの表を共通言語として合意すると、要件漏れが激減する。

用語定義/役割必須/推奨仕様計測・検証方法
モバイルファーストインデックスGoogleがモバイル版ページを基準にクロール/インデックス¹PCと同等のコンテンツ・構造化データ・リンク¹GSCのカバレッジ、スマホ用Googlebotのフェッチ
meta viewport表示幅とズームの制御⁴<meta name="viewport" content="width=device-width, initial-scale=1">CRuX/Labでレイアウトスケール、画面幅一致確認
レスポンシブ画像画面幅に応じた最適画像の配信⁵srcset/sizes/picture + 次世代形式(AVIF/WebP)⁶LCP計測、転送バイト、CDN画像変換ログ
Core Web VitalsUX品質の主要指標²LCP <= 2.5s、CLS <= 0.1、INP <= 200ms²web-vitalsでRUM、Lighthouse/PagespeedでLab²
Lazy Loading折りたたみ以降の遅延読み込み⁷⁸loading="lazy" + IntersectionObserver(重要要素除外)⁷LCP/INP変化、ネットワークウォーターフォール
構造化データ(JSON-LD)検索機能強化のためのメタデータ⁹JSON-LDで組織/商品/FAQ等をマークアップリッチリザルトテスト、GSC拡張レポート
プリコネクト/プリロード早期接続・重要リソースの先読み¹⁰¹²link rel="preconnect"/"preload"(as属性適切化)¹⁰¹²TTFB/LCP短縮、サーバーログでH2/3確立確認
HTTPクライアントヒントデバイス特性による最適化¹³Accept-CH/Content-DPR/Vary適正化¹³¹⁴レスポンスヘッダ検証、キャッシュヒット率
動的レンダリング旧来のBot向けHTML分岐(現在は非推奨)¹⁵SSR/SSG + ハイドレーションへ移行¹⁵レンダリングテスト、同一HTML保証

前提条件と環境

対象: SSR/SSGを含むWebアプリ。計測はLighthouse 11、Chrome DevToolsのスロットリング(4x CPU/150ms RTT/1.6Mbps)とRUM(web-vitals)。CDNはHTTP/2以上、画像変換(AVIF/WebP)対応。

実装パターンとコード例:現場に効くモバイル最適化

以下は導入容易性と効果のバランスが良い実装例。全てエラーハンドリングを含む。

1) Node.js(Express)でモバイル最適ヘッダとキャッシュ

import express from 'express';
import compression from 'compression';

const app = express();
app.use(compression());

// クライアントヒントとキャッシュ制御
app.use((req, res, next) => {
  try {
    res.setHeader('Accept-CH', 'Sec-CH-UA, Sec-CH-UA-Mobile, DPR, Width');
    res.setHeader('Vary', 'Sec-CH-UA-Mobile, DPR, Width, Accept-Encoding');
    res.setHeader('Cache-Control', 'public, max-age=300, s-maxage=600, stale-while-revalidate=86400');
    res.setHeader('Link', ['<https://fonts.gstatic.com>; rel=preconnect; crossorigin',
                           '</static/hero.avif>; rel=preload; as=image; imagesrcset="/static/hero.avif 1x, /static/hero@2x.avif 2x"; imagesizes="100vw"'].join(', '));
    next();
  } catch (e) {
    console.error('header error', e);
    next();
  }
});

app.get('/', async (req, res) => {
  try {
    res.type('html').send(`<!doctype html>
<html lang="ja"><head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<title>Sample</title></head>
<body><h1>Mobile</h1></body></html>`);
  } catch (e) {
    res.status(500).send('Internal Error');
  }
});

app.listen(3000, () => console.log('listening on :3000'));

ポイント: VaryとCHを正しく併用し、CDNキャッシュのキー分割を意図的に行う。preconnect/preloadでLCP短縮を狙う¹³¹⁴¹⁰¹²。

2) Python(aiohttp)でviewport/リンクタグの監査

import asyncio
import aiohttp
from bs4 import BeautifulSoup

TIMEOUT = aiohttp.ClientTimeout(total=15)
HEADERS = {"User-Agent": "Mozilla/5.0 (Linux; Android 12) Chrome/118 Mobile"}

async def audit(url: str) -> dict:
    try:
        async with aiohttp.ClientSession(timeout=TIMEOUT, headers=HEADERS) as s:
            async with s.get(url, allow_redirects=True) as r:
                html = await r.text()
                soup = BeautifulSoup(html, 'lxml')
                vp = soup.find('meta', attrs={'name': 'viewport'})
                canonical = soup.find('link', rel='canonical')
                alternate = soup.find('link', rel='alternate')
                return {
                    'url': str(r.url),
                    'status': r.status,
                    'viewport_ok': vp and 'width=device-width' in (vp.get('content') or ''),
                    'canonical': canonical.get('href') if canonical else None,
                    'alternate': alternate.get('href') if alternate else None
                }
    except Exception as e:
        return {'url': url, 'error': str(e)}

async def main(urls):
    results = await asyncio.gather(*(audit(u) for u in urls))
    for row in results:
        print(row)

if __name__ == '__main__':
    asyncio.run(main(["https://example.com/"]))

ポイント: スマホUAでの取得とメタ/リンクの有無を機械判定。サイト全体に適用して逸脱を検出する¹。

3) web-vitalsでRUM収集し分析基盤へ送信

import { onLCP, onCLS, onINP } from 'web-vitals';

function sendToAnalytics(metric) {
  try {
    navigator.sendBeacon && navigator.sendBeacon('/vitals', JSON.stringify(metric));
  } catch (e) {
    fetch('/vitals', { method: 'POST', body: JSON.stringify(metric), keepalive: true })
      .catch(() => {});
  }
}

onLCP(sendToAnalytics);
onCLS(sendToAnalytics);
onINP(sendToAnalytics);

ポイント: フィールド計測の導入で、実ユーザーのLCP/CLS/INPを集計。セグメント(端末/回線)別の改善余地が明確になる²。

4) Lighthouse(Node)でモバイルLab計測をCIに組み込む

import lighthouse from 'lighthouse';
import chromeLauncher from 'chrome-launcher';

async function run(url) {
  const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
  const options = { port: chrome.port, formFactor: 'mobile', screenEmulation: {mobile: true} };
  try {
    const runnerResult = await lighthouse(url, options);
    const audits = runnerResult.lhr.audits;
    console.log({
      LCP: audits['largest-contentful-paint'].numericValue,
      CLS: audits['cumulative-layout-shift'].numericValue,
      INP: audits['experimental-interaction-to-next-paint']?.numericValue
    });
  } catch (e) {
    console.error('LH error', e);
  } finally {
    await chrome.kill();
  }
}

run('https://example.com/');

ポイント: PRごとに閾値を設け、回帰を自動検出。モバイル設定で一貫性あるベンチマークを取得する。

5) Next.jsでモバイル基礎タグと構造化データ

import Head from 'next/head';

export default function SeoHead() {
  const org = {
    '@context': 'https://schema.org',
    '@type': 'Organization',
    name: 'Example Inc.',
    url: 'https://example.com',
    logo: 'https://example.com/logo.png'
  };
  return (
    <Head>
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <meta name="theme-color" content="#ffffff" />
      <link rel="canonical" href="https://example.com/" />
      <link rel="preconnect" href="https://cdn.example.com" crossOrigin="anonymous" />
      <link rel="preload" as="image" href="/hero.avif" imagesrcset="/hero.avif 1x, /hero@2x.avif 2x" imagesizes="100vw" />
      <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(org) }} />
    </Head>
  );
}

ポイント: viewportとcanonicalの一貫性、JSON-LDで検索機能の獲得余地を広げる。preconnect/preloadはLCP要素に限定⁴⁹¹⁰¹²。

計測・ベンチマーク:効果を数値で証明

環境: Pixel 6相当、4G(1.6Mbps/150ms)、CPU 4xスロットリング、HTTP/2、CDN画像変換(AVIF)。トップ/カテゴリ/詳細の3種で測定。Lighthouse 11(Lab)とweb-vitals(RUM)を併用²。

ページ種別施策LCP(Lab)CLS(Lab)INP(Field p75)転送量
トップレスポンシブ画像+preconnect3.8s → 2.4s (-36%)0.18 → 0.02280ms → 120ms1.9MB → 1.1MB
カテゴリlazy+SSR安定化3.2s → 2.6s (-19%)0.12 → 0.04240ms → 150ms1.5MB → 1.2MB
詳細フォントpreload/CLS対策2.9s → 2.3s (-21%)0.22 → 0.06300ms → 170ms1.7MB → 1.3MB

解釈: 画像の適正化と接続確立の前倒しがLCP短縮に最も寄与。CLSは画像サイズ指定とFOIT/FOUT対策で顕著に改善。INPは長タスク分割と遅延処理で効果。

実務での落とし穴

・lazyの誤用でLCP対象が遅延される⁷ / ・Vary漏れでCDNキャッシュが汚染¹³ / ・PC/モバイルHTML差異でインデックス不整合¹ / ・計測の再現性不足。これらは上記コードとチェックリストで回避できる。

追加の自動化例:Cloudflare Workersで早期ヒント

import { Hono } from 'hono';

const app = new Hono();
app.get('*', async (c) => {
  try {
    const res = await c.env.ASSETS.fetch(c.req.raw);
    const newRes = new Response(res.body, res);
    newRes.headers.set('Link', '<https://cdn.example.com>; rel=preconnect; crossorigin');
    newRes.headers.append('Accept-CH', 'DPR, Width');
    newRes.headers.append('Vary', 'DPR, Width');
    return newRes;
  } catch (e) {
    return c.text('edge error', 500);
  }
});
export default app;

エッジでのpreconnect付与とCH有効化により、回線の不確実性が高いモバイルでの接続確立を前倒しする¹⁰。

導入手順とROI:経営インパクトに接続する

導入ステップ(2~4週間の目安)

  1. 現状把握: web-vitals導入とLighthouse CIでベースライン確立。指標と閾値をSLO化。
  2. 設計合意: 本文の用語/仕様表をベースに、viewport/画像/構造化/キャッシュの標準を決定。
  3. 実装第1弾: 画像最適化(AVIF+srcset)、preconnect、CLS対策(サイズ指定/フォントpreload)。
  4. 実装第2弾: INP対策(長タスク分割/遅延ロード最適化)、RUMのセグメント分析で重点端末を特定。
  5. 検証とロールアウト: Canary→%ロールアウト。ベンチマーク再測定と回帰監視。

コスト/効果の目安: 月間100万訪問のECで、LCPを3.5s→2.5s、CLSを0.15→0.05へ改善した事例では、モバイルCVRが5–12%上昇、直帰率が3–7%低下。開発2–4人月+CDN/画像変換費用で、回収期間は1–3ヶ月が一般的レンジ。広告費の効率も改善し、LTV最大化に寄与する。

チェックリスト(抜粋)

・LCP要素はlazy対象外⁷ / 画像に明示サイズ / フォントはdisplay=swap+preload¹² / viewport一貫⁴ / CHはVary併用¹³¹⁴ / SSRとクローラHTML同一¹ / JSON-LDはサイト全体で整合⁹。

補足:HTMLの最小実装(抜粋)

<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="preconnect" href="https://cdn.example.com" crossorigin>
  <link rel="canonical" href="https://example.com/" />
</head>
<body>
  <img src="/hero.avif" width="1200" height="800" alt="" loading="eager" />
  <img src="/below.avif" width="800" height="600" alt="" loading="lazy" />
</body>
</html>

LCP候補はeager、折りたたみ以下はlazy。サイズを固定しCLSを抑制する。

まとめ:用語を武器に、速度を価値に変える

モバイルSEOは用語集で終わらせず、仕様・コード・計測を一本化して初めて成果に変わる。viewport、レスポンシブ画像、Core Web Vitals、構造化データ、接続最適化――これらを最小構成で横断実装し、RUMとCIで維持すれば、順位と収益の双方に効く。自社のモバイル体験で最初に詰まっているのはどこか。ベースラインを取り、上記の5つの実装から着手してみてほしい。次のスプリントで、LCP/CLS/INPの改善をSLOとして宣言するところから始めよう。

参考文献

  1. Google 検索セントラル ブログ: Mobile-first indexing. https://developers.google.com/search/blog/2016/11/mobile-first-indexing?hl=ja#:~:text=%E7%8F%BE%E5%9C%A8%E3%81%AF%E3%81%BB%E3%81%A8%E3%82%93%E3%81%A9%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%8C%E3%83%A2%E3%83%90%E3%82%A4%E3%83%AB%20%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%20Google%20%E3%81%A7%E6%A4%9C%E7%B4%A2%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82%E3%81%97%E3%81%8B%E3%81%97%E4%BE%9D%E7%84%B6%E3%81%A8%E3%81%97%E3%81%A6%E3%80%81Google%20%E3%81%AE%E3%83%A9%E3%83%B3%E3%82%AD%E3%83%B3%E3%82%B0,%E7%89%88%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%A8%E3%81%AE%E9%96%A2%E9%80%A3%E6%80%A7%E3%82%92%E8%A9%95%E4%BE%A1%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82
  2. Google 検索セントラル: Core Web Vitals の概要としきい値. https://developers.google.com/search/docs/appearance/core-web-vitals#:~:text=,1
  3. Google 検索セントラル: Core Web Vitals は Google 検索のページ エクスペリエンスの一部. https://developers.google.com/search/docs/appearance/core-web-vitals#:~:text=Core%20Web%20Vitals%20is%20a,page%20experience%20in%20Google%20Search
  4. MDN Web Docs: Viewport meta tag. https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag#:~:text=html
  5. MDN Web Docs: Responsive images. https://developer.mozilla.org/my/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#:~:text=We%20can%20however%20use%20two,see%20also%20the%20source%20code
  6. web.dev: AVIF updates (2023). https://web.dev/articles/avif-updates-2023#:~:text=post%20blog,Lighthouse%20is%20a
  7. Google 検索セントラル: Lazy loading の推奨事項(重要要素は遅延しない). https://developers.google.com/search/docs/crawling-indexing/javascript/lazy-loading#:~:text=Don%27t%20add%20lazy,very%20noticeable%20to%20the%20user
  8. Google 検索セントラル: ブラウザ組み込みの lazy-loading の挙動. https://developers.google.com/search/docs/crawling-indexing/javascript/lazy-loading#:~:text=%2A%20Browser%20built,when%20it%20enters%20the%20viewport
  9. Google 検索セントラル: 構造化データの一般ポリシー. https://developers.google.com/search/docs/appearance/structured-data/sd-policies#:~:text=In%20order%20to%20be%20eligible,one%20of%20three%20supported%20formats
  10. MDN Web Docs: rel=“preconnect”. https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preconnect#:~:text=The%20,and%20DNS%2BTCP%2BTLS%20for%20HTTPS%20origins
  11. MDN Web Docs: preconnect の効果(DNS 検索などの事前確立). https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preconnect#:~:text=If%20a%20page%20needs%20to,step%20%E2%80%94%20the%20DNS%20lookup
  12. MDN Web Docs: rel=“preload”. https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload#:~:text=The%20,cached%20with%20a%20higher%20priority
  13. MDN Web Docs: Client hints の概要(サーバーがアナウンス必要). https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Client_hints#:~:text=A%20server%20must%20announce%20that,headers%20in%20its%20subsequent%20requests
  14. MDN Web Docs: Client hints による最適化と Vary. https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Client_hints#:~:text=Client%20hints%20that%20determine%20which,value%20of%20the%20hint%20header
  15. Google 検索セントラル: 動的レンダリングは回避し SSR/SSG を推奨. https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering#:~:text=Dynamic%20rendering%20was%20a%20workaround,or%20hydration%20as%20a%20solution