はじめに:Lighthouseの「甘い警告」が招く本番の惨劇

Webサイトのパフォーマンス改善に取り組むフロントエンドエンジニアなら、LighthouseやPageSpeed Insightsの診断結果で一度は次のメッセージを目にしたことがあるはずだ。

「重要なリクエストを事前読み込み(preload)してください」

このアドバイスに従い、HTMLの <head> 内に Web フォントの <link rel="preload" as="font" ...> を記述する。手元の開発マシンの光回線で再計測すると、警告が消えてスコアが数ポイント上昇し、「これでパフォーマンス改善は完璧だ」と胸をなでおろす。

だが数週間後、実ユーザー環境のメトリクス(Chrome User Experience Report: CrUX)を確認して愕然とすることになる。LCP(Largest Contentful Paint)が「良好(緑)」から「要改善(黄)」、ひどい場合は「不良(赤)」へ大幅に悪化しているのだ。

「フォントを先読みして描画を速くしたはずなのに、なぜ最も重要な要素の表示が遅れるのか?」——そこには、ブラウザの内部ネットワークスケジューリングと、リソース同士による過酷な帯域強奪のメカニズムが存在している。

ブラウザの優先度制御キューと「帯域強奪」の物理的現実

ブラウザがWebページを読み込む際、すべてのリソースが平等に扱われるわけではない。Chromiumをはじめとする主要ブラウザは、内部で綿密な「Resource Fetch Priority(リソース取得優先度)」を管理している。

Chromiumにおける Resource Fetch Priority の実態

通常、CSSファイル内の @font-face で指定されたWebフォントは、HTML解析とCSSOM構築が完了し、実際にそのフォントが画面上のレンダリングツリーで必要とされるまでダウンロードが開始されない。この遅延による「テキストのちらつき(FOUT)」や「不可視テキスト(FOIT)」を防ぐために導入されたのが preload だ。

しかし、HTML先頭に <link rel="preload" as="font"> を配置すると、ブラウザのプリロードスキャナ(Preload Scanner)はリソースの優先度を強制的に「VeryHigh(最優先)」に引き上げる。これは Critical CSS やファーストビューのHTMLと同等の極めて高い優先順位だ。

帯域飽和とLCP画像の道連れブロック

この「最優先」指定が、特にモバイル4G環境や公衆Wi-Fiなどの低速・高レイテンシ回線において致命的なボトルネックを生み出す。

TCP接続の初期フェーズには「TCPスロースタート」という制約があり、接続開始直後は一度に送信できるデータ量(輻輳ウィンドウサイズ)が物理的に制限されている。その極めて貴重で細い初期帯域に、数百KBから数MBにおよぶ重いWOFF2ファイルのリクエストが最優先でねじ込まれるのだ。

その結果、何が起きるか。ファーストビューで最も重要な「Hero Image(メインビジュアル画像)」や、レンダリングをブロック解除するための「Critical CSS」「メインバンドルJS」のパケット転送が完全に後ろへ追いやられる。

「テキストのタイポグラフィを美しく整えるための装飾品」が帯域を独占し、「ユーザーがページを開いて真っ先に見たい主役コンテンツ」の表示を数秒間も遅延させる。これが、安易なフォントpreloadが引き起こす「LCP破壊」の正体である。

現場で頻発する「Webフォントpreloadの3大即死トラップ」

優先度の問題だけではない。実際のコードベースをレビューすると、さらに破壊的なアンチパターンが平然と本番稼働しているケースが後を絶たない。

1. crossorigin 属性の欠落による「二重ダウンロード」

Webフォント仕様(W3C)において、フォントフェッチはセキュリティ上の理由(CORS)から常に「無名クレデンシャルモード(anonymous CORS)」で実行されなければならない。そのため、preloadタグには必ず crossorigin 属性を付与するルールになっている。

もしこれを忘れるとどうなるか。ブラウザは preload 時に通常の同一オリジンリクエストとしてフェッチし、その後のCSSレンダリング時に「CORSモードが一致しない」と判断して全く同じフォントファイルをもう一度ネットワーク越しにダウンロードする。

<!-- ❌ 即死アンチパターン: crossorigin 属性が抜けている -->
<link rel="preload" href="/fonts/NotoSansJP-Regular.woff2" as="font" type="font/woff2">

<!-- 
  結果:
  1回目: Preloadとしてダウンロード(未使用で破棄される)
  2回目: CSSの @font-face から再度ダウンロード
  → 貴重なモバイル帯域を2倍浪費し、LCPを自ら爆破する
-->

Chromeのデベロッパーツール(Consoleタブ)にひっそりと警告が出るものの、ビルドエラーにならないため、多くの現場で見落とされたまま本番トラフィックを圧迫し続けている。

2. 未使用ウェイト・全文字フルセットの無分別なpreload

「ファーストビューでは見出しのBold(700)しか使っていないのに、本文用のRegular(400)やLight(300)、果てはItalicまでまとめてpreloadしている」というコードも日常茶飯事だ。

特に日本語フォントは、第一水準・第二水準漢字を含むと数千〜数万グリフにおよび、フルセットのWOFF2ファイルは1ファイルあたり1MB〜3MBを超える。これを3ウェイトもpreloadすれば、初回ロード時に5MB近い巨大データが最優先キューに突っ込まれることになる。モバイル回線であれば、この時点でCore Web Vitalsのゲームオーバーは確定する。

3. font-display: block との組み合わせによるFOIT地獄

フォント読み込み中のチラつきを極端に嫌うあまり、@font-face に font-display: block を指定したまま preload を設定する構成も危険極まりない。

もし回線が詰まってフォントの到着が遅れると、最大3秒間にわたってテキスト全体が完全に透明化(Flash of Invisible Text: FOIT)する。この間、ユーザーの画面には何も表示されず、ブラウザは文字ブロックをLCP要素として認識できないため、LCPの計測時間はそのままフォント到着まで引き延ばされる。

現場で生き残るための「脱・脳死preload」実践アーキテクチャ

では、Webタイポグラフィの美しさとCore Web Vitalsの数値を両立させるには、どう設計すべきなのか。答えは「引き算」と「Modern CSSの活用」にある。

原則1: preloadは「サブセット化された1ウェイト(50KB以下)」に限定する

preloadしてよいフォントは、「ファーストビューの見出し(H1)で確実に使われる1ウェイト」かつ「常用漢字・英数記号に厳密に絞り込んだサブセットフォント」のみである。

<!-- ⭕ 生き残る設計: サブセット化・単一ウェイト・crossorigin必須 -->
<link rel="preload" 
      href="/fonts/heading-subset.woff2" 
      as="font" 
      type="font/woff2" 
      crossorigin>

サブセット化によってファイルサイズを30KB〜50KB程度に抑え込めば、初期TCPウィンドウ内でも瞬時に転送が完了し、後続のLCP画像やCSSの帯域を阻害しない。

原則2: 本文テキストには font-display: optional を採用する

記事本文やUIの汎用テキストに対しては、preloadを完全に廃止し、font-display: optional を採用するのが最も堅牢だ。

font-display: optional は、ブラウザが接続状況を判断し、最初の約100ms以内にフォントが到着しなければ即座にシステムフォールバックフォントを描画する。Webフォントのダウンロード自体はバックグラウンドで続行され、ブラウザキャッシュに保存されるため、2ページ目以降の遷移やリピート訪問時にはゼロ遅延で美しいWebフォントが適用される。

「初回訪問時のLCPとCLSを1ミリ秒も損なわず、2回目以降でWebフォントの恩恵を最大化する」という、実用主義に徹した設計だ。

原則3: Modern CSSの size-adjust でフォールバックのCLSをゼロにする

多くのエンジニアが無理にpreloadを乱用してしまう根本的な原因は、「フォールバックフォントからWebフォントに切り替わる瞬間のカクつき(レイアウトシフト/CLS)」を恐れているからだ。

しかし現在では、Modern CSSのフォントメトリクス記述子(size-adjust, ascent-override, descent-override)を使用することで、システム標準フォントの字幅・高さをWebフォントにピクセル単位で同期させることができる。

/* Webフォント本体(swapで即時フォールバックを描画) */
@font-face {
  font-family: 'BrandSans';
  src: url('/fonts/heading-subset.woff2') format('woff2');
  font-display: swap;
}

/* システムフォールバックのメトリクスを微調整し、切り替え時のズレを根絶する */
@font-face {
  font-family: 'BrandSans-Fallback';
  src: local('Hiragino Sans'), local('Yu Gothic'), local('Meiryo'), sans-serif;
  size-adjust: 98.2%;
  ascent-override: 95%;
  descent-override: 22%;
  line-gap-override: 0%;
}

/* スタイル適用 */
h1, .hero-title {
  font-family: 'BrandSans', 'BrandSans-Fallback', sans-serif;
}

フォールバックフォントの描画領域とWebフォントの描画領域が完全に一致するため、フォントが非同期で読み込まれて切り替わったとしても、テキストの再折り返し(Reflow)やガタつきが一切発生しない。preloadに頼ることなく、CLSスコアを物理的に 0.00 に固定できるのだ。

おわりに:Webフォントは「コンテンツ」ではなく「装飾」である

パフォーマンスチューニングの本質とは、「何を先読みするか」ではなく「何を諦めて後回しにするか」の優先順位付けにある。

ユーザーがあなたのWebサイトに訪れるのは、美しいフォントを鑑賞するためではない。そこに書かれている情報や提供されている価値を求めてやってくるのだ。装飾のために細いモバイル回線の帯域を占有させ、主役であるコンテンツの到着を待たせる設計は、ユーザーファーストとは呼べない。

Lighthouseのスコアを盲目的に追うのをやめ、ブラウザのキューの奥底で何が起きているのかに目を向けよう。無駄な preload を削ぎ落とし、CSSでレイアウトシフトを制御するミニマルな設計こそが、現場で本当に愛される高速なWebフロントエンドを作り上げる。