by MintJams

思考を先に読む!Speculation Rulesでナビゲーションを瞬時にするガイド

ナビゲーションをほぼ瞬時にするSpeculation Rulesの完全ガイド。prerenderとprefetchの使い分け、Next.jsでの実装方法、Chrome DevToolsでのデバッグ手順、そして実装時の注意点までを詳しく解説します。

ユーザーがウェブサイトのリンクをクリックしたとき、「読み込み中...」と表示される待ち時間は、ユーザー体験を損なう大きな要因です。この待ち時間をなくす究極のパフォーマンス改善策が、事前レンダリング(prerender)です。

しかし、事前レンダリングはリソースを大量に消費するため、「当たる」(ユーザーが実際に遷移する)リンクに絞って実行する必要があります。この問題を解決するのが、ブラウザに事前処理のルールを指示するSpeculation Rulesです。

本記事では、このSpeculation Rulesを使い、Next.jsなどのフレームワークと組み合わせて、ナビゲーションをほぼ瞬時にする方法を解説します。


この記事でわかること

  • Speculation Rulesの基本的な仕組みとprerender / prefetchの違い
  • Next.js App Routerでの具体的な実装パターン
  • 事前レンダリングが成功したかどうかをデバッグする方法
  • 帯域やリソースを無駄にしないためのルール設計のコツ

1. Speculation Rulesとは:ブラウザの先読みを制御するJSON

Speculation Rulesは、ブラウザが次にどのページに遷移するかを予測し、事前にリソースを読み込む、あるいはページ全体をレンダリングしておくためのルールを定義する仕組みです。

このルールは、HTML内にインラインで記述された<script type="speculationrules">タグ、またはSpeculation-RulesというHTTPヘッダーを通じてブラウザに伝えます。

主なアクションには、以下の2種類があります。

アクション 説明 コストと効果
prefetch 遷移先の文書(HTML)のみを先に取得します。 コスト小、効果中。通信待ち時間を短縮します。
prerender 遷移先のページ全体を非表示のタブでレンダリングします。HTML、CSS、JavaScript、画像などの取得から実行まで行います。 コスト大、効果大。ナビゲーションがほぼ瞬時に完了します。

コストの大きさから、prerenderはユーザーが遷移する可能性が非常に高いリンクに限定して適用するのが鉄則です。

ブラウザ対応とプログレッシブエンハンスメント

現在、Speculation RulesChromium系のブラウザ(Chrome、Edge)121以降で本格的に利用可能です。FirefoxやSafariはまだ未対応のため、あくまでパフォーマンス向上のためのプログレッシブエンハンスメントとして導入することが前提となります。

非対応ブラウザ向けには、HTMLScriptElement.supports("speculationrules")で機能の有無を判定し、従来の<link rel="prefetch">に切り替えるフォールバックを実装しましょう。

// Speculation Rulesのサポートを判定
if (!HTMLScriptElement.supports("speculationrules")) {
  // 非対応ブラウザ向けのフォールバック処理
  const link = document.createElement('link');
  link.rel = 'prefetch';
  link.href = '/next-page';
  document.head.appendChild(link);
}

2. 実装ガイド:Next.js App Routerでの活用

ここでは、Next.js App Router環境を想定した具体的な実装手順を見ていきます。

2-1. 最小導入(インラインrules)

app/layout.tsxに、以下のようにspeculationrulesを定義します。これにより、サイト全体のルールを一度に設定できます。

import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Next.js + Speculation Rules",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <head>
        <script
          type="speculationrules"
          dangerouslySetInnerHTML={{
            __html: JSON.stringify({
              prerender: [{
                where: { selector_matches: 'a[data-prerender="true"]' },
                eagerness: "moderate"
              }]
            }),
          }}
        />
      </head>
      <body>{children}</body>
    </html>
  );
}
  • where: selector_matcheshref_matchesを使って、どのリンクを対象にするかを指定します。
  • eagerness: ブラウザがどのタイミングで事前処理を開始するかを調整します。

2-2. コンポーネント側での指定

先ほどのルール定義と組み合わせて、prerenderしたいリンクにdata-prerender="true"を付与します。

import Link from "next/link";

export function ProductCard({ id, title }: { id: string; title: string }) {
  return (
    // ユーザーがクリックしそうなリンクにだけ指定
    <Link href={`/products/${id}`} data-prerender="true">
      <h3>{title}</h3>
    </Link>
  );
}

3. 配信方法とセキュリティ

Speculation-Rulesヘッダーでの配信(推奨)

大規模サイトでは、ルールを外部JSONファイルとして管理し、Speculation-Rulesヘッダーで配信するのが効率的です。

  1. JSONファイルの作成: rules/spec.jsonのようなファイルを作成し、ルールを記述します。
  2. MIMEタイプ: サーバー設定で、このJSONファイルのMIMEタイプをapplication/speculationrules+jsonに設定します。
  3. HTTPヘッダー: ページのレスポンスヘッダーにSpeculation-Rules: /rules/spec.jsonを付与します。

CSPと同一オリジン制限

  • CSP: インラインの<script>タグを使う場合は、Content-Security-Policy: script-src 'self' 'inline-speculation-rules' ...を追加します。
  • 同一オリジン制限: prerenderは基本的に同一オリジン(same-origin)のページに限定されます。同一サイト(same-site)のクロスオリジンの場合でも、遷移先のサーバーがSupports-Loading-Mode: credentialed-prerenderヘッダーで明示的にオプトインしなければprerenderは実行されません。クロスサイト(cross-site)の事前レンダリングは完全に不可です。

4. サーバー側での検知と制御

投機的なプリロードは、サーバー側から見ると通常のアクセスと区別がつきにくいことがあります。Sec-Purposeヘッダーを使って、ログの分離やリソースの制御を行いましょう。

  • ログの分離: prerenderprefetchによるリクエストには、Sec-Purpose: prefetch;prerenderヘッダーが付与されます。サーバー側でこのヘッダーを検知し、通常のアクセスログとは分けて集計することで、リソースの消費状況を正確に把握できます。
  • キャッシュのクリア: 開発時や本番運用で問題が発生した場合、Clear-Site-Data: "prefetchCache", "prerenderCache"ヘッダーを送信することで、ブラウザの投機結果キャッシュを強制的にクリアできます。

安全な除外ルールの具体例

カート追加やログアウトなど、ユーザー状態を変更する可能性のあるページは、誤ったprerenderを防ぐためにルールから明示的に除外すべきです。

{
  "prerender": [
    {
      "where": {
        "selector_matches": "a[data-prerender='true']"
      },
      "href_matches": ["/*"]
    }
  ],
  "prerender_not_for_href_matches": ["/logout", "/cart/*", "/checkout/*", "?add-to-cart=*"]
}

また、requiresプロパティを使って、クロスオリジンのリソースを匿名クライアントからのアクセスに限定するなどの制御も可能です。


5. デバッグと「動かない時」の一次切り分け

DevToolsでの確認

Chrome DevToolsのApplication → Speculative loadsパネルは、事前レンダリングのデバッグに欠かせません。成功・失敗のステータスだけでなく、なぜ失敗したのかの理由(例: Prerendering was cancelled because a popup was opened.)もここで確認できます。

動かない時のチェックポイント

  • ブラウザ設定: Chromeの「ページをプリロードする」設定がオフになっていないか確認しましょう。
  • メモリ・バッテリー: ユーザーのデバイスが低メモリ状態だったり、バッテリーセーバーが有効になっていたりすると、ブラウザはprerenderを抑制することがあります。
  • 機能制限: prerender中はalert()などのポップアップ系APIが無効化されます。ログインフォームなど、インタラクションが必須なページはprerenderの対象から外しましょう。また、cross-originiframeはアクティベーションされるまでレンダリングが遅延されるため注意が必要です。

まとめ:Speculation Rules導入チェックリスト

「当たるリンクだけをprerender」――これが要点です。小さく始めて計測し、効果が出た導線にだけ段階的に適用。下のチェックリストで、安全に導入を完了させましょう。

  • prerenderprefetchのルールは、selector_matches意図の高いリンクのみを対象にする。
  • CSPに'inline-speculation-rules'を追加するか、ヘッダー配信にする。
  • Speculation-Rulesヘッダーを配信する場合は、MIMEタイプをapplication/speculationrules+jsonに設定する。
  • href_matchesprerender_not_for_href_matchesログアウトやカートなど副作用のあるURLを明示的に除外する。
  • Chrome DevToolsSec-Purposeヘッダーを使って、正しく機能しているか検証する。
  • 非対応ブラウザ向けに、HTMLScriptElement.supports()を使ったフォールバック処理を実装する。

この記事が、あなたのウェブサイトのナビゲーション速度改善の一助となれば幸いです。ご覧いただきありがとうございました。