フォントの読み込み

Webフォントの読み込みがCLSやLCPなどのCore Web Vitalsに与える影響、font-displayの戦略、フォントのプリロード、フォント切り替えによるレイアウトシフトの抑え方を解説します。

カスタムWebフォントは、テキストをその書体で描画する前にダウンロードが必要な外部ファイルです。その間に何を表示するかを、主にCSSのfont-displayプロパティで決めます。この挙動は、レンダリングを妨げるフォントが最大のテキスト表示を遅らせるLCPと、代替フォントからWebフォントへの切り替えが配置を動かすCLSに影響します。FOITとFOUTという2つの基本挙動には、それぞれ弱点があります。重要なフォントをプリロードする、font-display: optionalまたはswapを意図的に使う、代替フォントのメトリクスを合わせる、あるいはカスタムフォントを使わないことが確実な対策です。フォント読み込み自体は直接のランキング要因ではなく、ページエクスペリエンスのシグナルであるCLS/LCPに影響します。

要点 — カスタムWebフォントはレンダリングに影響する外部リソースです。レンダリングを妨げるフォントが最大のテキスト要素を遅らせればLCPに、代替フォントからWebフォントへの切り替えがレイアウトを再配置すればCLSに影響します。基本挙動のFOIT(読み込みまで非表示)とFOUT(代替フォントを表示してから切り替え)は、異なる形で問題になります。主な調整手段はfont-displayです(swapはFOUTを確実に起こし、optionalは約100msの猶予後に使用フォントを確定して、その後の切り替えをなくします)。プリロードはフォントの検出遅延を解消し、**プリロード+font-display: optional**はGoogleのエンジニアリング記事がレイアウトのがたつきをなくす方法として特に挙げています。ただしLighthouseのfont-displayチェックに合格してもCLSは直りません。メトリクスの近い代替フォント(またはsize-adjust/ascent-override)が必要です。フォント読み込み自体は直接のランキング要因ではなく、ページエクスペリエンスのシグナルであるCLS/LCPに作用します。

この主張の根拠 The CSS Font Loading API exposes font loading state and control to documents. 対象範囲: Current official or standards documentation. 信頼度: 高 · 検証日: MDN: CSS Font Loading API この主張の根拠 font-display controls how a font face is displayed while it downloads and when fallback is used. 対象範囲: Current official or standards documentation. 信頼度: 高 · 検証日: MDN: font-display

フォントが特殊な理由

多くのパフォーマンス問題はバイト数に関係します。画像が大きすぎる、スクリプトがメインスレッドを塞ぐ、といったものです。フォントはある一点でさらに厄介です。外部リソースであるうえに、テキストのメトリクスも変えるからです。そのため、テキストの描画を遅らせる(LCPの問題)ことと、表示後にテキストの大きさや形を変える(CLSの問題)ことを同時に引き起こします。

この話全体の土台となる、ブラウザの2つの基本挙動を押さえておきましょう。

  • FOIT(Flash of Invisible Text): カスタムフォントが読み込まれるかタイムアウトするまで、そのフォントのテキストを非表示で描画します。従来、多くのブラウザでブロック期間は最大約3秒でした。
  • FOUT(Flash of Unstyled Text): 代替フォントをすぐ表示し、Webフォントが読み込まれた時点で置き換えます。

FOITは「きれい」(跳ねない)に見える一方、描画遅延の危険があります。FOUTは速く(テキストがすぐ)感じられる一方、切り替え時のレイアウトシフトが問題になります。どちらも自動的に安全ではない、というのが核心です。

処理を別々の段階に分けて考えることも重要です。ある段階の成功が次の段階を保証するとは限りません。@font-face規則は書体を宣言するだけで、ブラウザが実際のフォントファイルを取得するのは、その書体に一致するスタイル付きテキストがページ上で必要になったときです(宣言とマッチングは取得とは別の段階です)。フォントを別オリジンから配信する場合、その取得はCORSの対象です。スタイルシートが問題なく読み込めても、フォントのレスポンス自体に適切なクロスオリジン許可があるとは限らず、ヘッダーが欠けるとフォントは表示されないまま静かに失敗します。さらに、取得に成功しても見た目が安定する保証はありません。コールドキャッシュ、ウォームキャッシュ、既にキャッシュ済みという状態ごとに検出や切り替えのタイミングが変わるため、DevToolsで再読み込みを1回試すだけでは初回訪問者の見え方は分かりません。

フォントがLCPに与える影響

ページで最も大きく見える要素が大きな見出しやヒーロー領域の段落などのテキストなら、そのテキストが使うフォントはLCP経路の一部になり得ます。web.devのLCPガイダンスによれば、ページにLCPリソースがある場合、それは画像かWebフォントのいずれかです。テキストを非表示にするレンダリング阻害フォント(FOIT)は、フォントが届くまでLCPを遅らせます。

対策は明快で、Googleが直接示しています。web.devのOptimize Largest Contentful Paint には次のようにあります。

“If you set a font-display value of anything other than auto or block, then text will always be visible during load, and LCP won’t be blocked on an additional network request.” (翻訳) 「font-displayにautoまたはblock以外の値を設定すると、読み込み中もテキストは常に表示され、追加のネットワーク要求によってLCPが妨げられることはありません。」 — web.dev

つまり、swap、fallback、optionalはいずれも読み込み中のテキストを表示し、LCPがフォント要求に拘束されるのを防ぎます。

フォントがCLSに与える影響

CLSは切り替えによって生じます。代替フォントとWebフォントでは文字幅や行の高さがほぼ必ず異なるため、一方を他方に置き換えるとテキストが再配置され、その下のすべてが動きます。これがレイアウトシフトです。

直感に反し、多くの人が見落とす点があります。FOITもCLSを引き起こします。 非表示のテキストも代替フォントのメトリクスで配置されるため、切り替えればやはり動きます。web.devのOptimize Cumulative Layout Shift には次のようにあります。

“Both approaches can cause layout shifts. Even if the text is invisible, it’s still laid out using the fallback font, so when the web font loads, the text block and the surrounding content shift” (翻訳) 「どちらの方法もレイアウトシフトを引き起こす可能性があります。テキストが非表示でも代替フォントを使って配置されるため、Webフォントが読み込まれるとテキストブロックと周囲のコンテンツが移動します。」 — web.dev

同じページでは2つの挙動を次のように説明しています。

“The fallback font is swapped with the web font, incurring a Flash of Unstyled Text (FOUT). ‘Invisible’ text is displayed using the fallback font until a web font is available and the text is made visible (FOIT—flash of invisible text).” (翻訳) 「代替フォントがWebフォントに置き換わることで、Flash of Unstyled Text(FOUT)が発生します。Webフォントが利用可能になってテキストが表示されるまで、『非表示』のテキストは代替フォントで表示されます(FOIT、flash of invisible text)。」 — web.dev

私もAhrefsのCumulative Layout Shift の記事で、より平易に同じことを述べています。フォントが読み込まれたり変わったりすると、FOITまたはFOUTという目立つシフトが起こります。これは、寸法を指定していない画像や読み込み後に挿入されるコンテンツと並び、現実によくあるCLS原因の一つです。

font-displayプロパティ

font-displayは@font-faceの記述子で、ブロック期間(フォントを待つ間にテキストを隠す時間)とスワップ期間(ブロック期間後も、到着したWebフォントへ切り替える時間)という2つの時間枠を制御します。web.devのBest Practices for Fonts によると次のとおりです。

値ブロック期間スワップ期間
autoブラウザ依存ブラウザ依存
block2〜3秒無限
swap0ms無限
fallback100ms3秒
optional100msなし

Google自身の文書が仕様の参照先としてリンクするMDNのfont-displayリファレンス は、簡潔に定義しています。swapは極めて短いブロック期間と無限のスワップ期間を持ち、optionalは極めて短いブロック期間を持つ一方、スワップ期間はありません。この「スワップ期間なし」こそoptionalの要点です。ブロック期間が終わると、その時点で使われているフォントが維持され、後から切り替わってレイアウトを動かすことがありません。

表の数値には注意が必要です。CSS仕様自体が定義するのは、short、extremely small、infinite、noneというブロック/スワップの期間分類だけで、正確な時間はブラウザに委ねられています。上のミリ秒・秒の値はweb.devが文書化したChromiumの挙動であり、ブラウザ横断の保証ではありません。仕様上の約束ではなく目安として扱い、実際にテストするブラウザ/バージョンの現行値を確認してください。

どのコンテンツにどの値を使うか。 web.devは値を使い分けられると助言しています。ブランドフォントの表示が重要で短いシフトを許容できるブランド要素や視覚的に特徴のある要素にはswap、CLSへの影響を優先しシフトを避けたい本文にはoptionalを使います。サイト全体に反射的に同じ値を設定しない、実用的な目安です。

フォントのプリロード

フォント要求は遅く検出されます。ブラウザが必要性を知るのは、CSSを解析し、ページ要素に@font-face規則を照合し、その要素が表示されると判断した後です。そこで初めてダウンロードが始まります。<link rel="preload">はこの経路を短縮します。

<link rel="preload" href="/fonts/brand.woff2" as="font" type="font/woff2" crossorigin>

これは、フォントが後で検出されるのを待たず、他の処理と並行して直ちに取得を始めるようブラウザへ伝えます。Bingのエンジニアリングチームも、自社検索ページでまさにこの仕組みを説明しています(詳細は「引用」タブ)。<head>内のプリロードタグがすぐにフォントのダウンロードを開始し、タグがなければブラウザはCSSを解析して一致する要素を見つけるまでフォントを取得しません。

Google自身の記事が挙げる最も強力な組み合わせは、**プリロード+font-display: optional**です。web.devのoptionalフォントのプリロードでレイアウトシフトとFOITを防ぐ は、<link rel="preload">とfont-display: optionalの併用を、カスタムフォント描画時のレイアウトのがたつきを確実になくす最も効果的な方法としています。またChromeではバージョン83以降、optionalフォントのプリロード時に起きていたレイアウトシフトが解消されたと述べています。プリロードは約100msの枠内にフォントが届く可能性を最大化し、間に合わなくてもoptionalがシフトを防ぎます。

プリロードしない方がよい場合。 プリロードは無料ではありません。web.devは、フォントを早期に検出させる効果が高い一方、他のリソース読み込みに使うブラウザ資源を奪うと警告しています。ファーストビューで本当に必要な1〜2個だけをプリロードしてください。所有するすべてのウェイトをプリロードすると、より重要なリソースが不足し、ページ全体が遅くなることがあります。

プリロードが実際に再利用されることを確認する。 プリロードが役立つのは、ブラウザが実際のフォント要求と一致させられる場合だけです。href、as="font"、type、crossoriginのモードは、CSSの@font-face規則が最終的に要求する内容と一致しなければなりません。違えば高速化どころか「unused preload」警告と無駄な2回目のダウンロードが発生します。プリロードはunicode-rangeの選択を迂回し、そのページに不要なサブセットを取得する場合もあります。DevToolsのNetworkパネルで一致を確認し、プリロードから始まった要求が1件だけで、2件になっていないことを確かめてください。

シフト自体を直す:代替フォントのメトリクスを合わせる

落とし穴があります。Lighthouseのfont-displayチェックに合格しても、CLSは直りません。GoogleのLighthouse文書は、一時的なシステムフォントがカスタムフォントに置き換わると、FOITとFOUTがCLSに与える影響は同じだと明記しています。swapはテキストを表示し(LCPと「非表示テキスト」監査には有効)、切り替えによるシフトは残します。

実際に切り替わるときのシフトをなくすには、代替フォントとWebフォントが同じ空間を占めるようにします。手段は2つです。

  1. font-familyスタックに、妥当な代替がない単独のカスタムフォント名ではなく、メトリクスが近いシステムフォントを選びます。代替フォントの文字幅と行高がWebフォントに近いほどシフトは小さくなります。
  2. @font-face規則でsize-adjust、ascent-override、descent-override、line-gap-overrideを使い、Webフォントのメトリクスを上書きして代替フォントのボックスに合わせます。web.devはこれらの記述子を列挙しており、徹底したい場合はSmashing MagazineのCSSフォント記述子の詳細解説 が参考になります。

4つの記述子はそれぞれ別のメトリクスを調整し、現在のブラウザ対応状況も記述子ごとに異なります。1つに依存する前に互換性を確認してください。実際に配信するウェイト、スタイル、文字体系でも検証が必要です。標準ウェイトのラテン文字で確認した修正が、太字、斜体、非ラテン文字でも同じように機能するとは限りません。

DebugBearのFixing Layout Shifts Caused by Web Fonts は、影響の計測、問題フォントの特定、font-displayの適用、より良い代替フォントの選択、フォントメトリクスの調整までを通して説明する優れた手引きです。

Google Fontsとセルフホスティング

font-displayを制御するためにセルフホスティングする必要はありません。Google Fonts CSS2 API はdisplayクエリパラメータを受け付け、https://fonts.googleapis.com/css2?family=Roboto&display=swap のように配信される@font-face規則へ直接font-displayを設定できます。Addy OsmaniのGoogle Fontsへのfont-display導入記事 では、以前はGoogle Fontsでfont-displayを指定する唯一の方法がセルフホスティングだったものの、この変更で不要になった経緯を説明しています。

セルフホスティングにはなお1つの強みがあります。fonts.googleapis.comとfonts.gstatic.comへの追加のDNS検索と接続をなくせることです。これは実際の遅延コストであり、Google Fontsを使う場合にそれらのオリジンへのpreconnectリソースヒント が役立つ理由でもあります。接続のオーバーヘッドと利便性を比較し、「Googleのものだから問題ない」とは考えないでください。コピーしたGoogle Fontsの埋め込みも、レンダリングを妨げるスタイルシート要求とフォント要求であり、古い埋め込みにはdisplay値がない場合もあります。

両者に普遍的な勝者はいません。実際に配信する文字体系とウェイト、キャッシュ設定、フォントのライセンス条件で決まります。チュートリアルの推奨をそのまま採用せず、自分のページで比較してください。

可変フォント

可変フォントは、標準、太字、幅狭、斜体など多くのウェイトやスタイルを、調整可能な軸を持つ1ファイルにまとめます。複数のバリエーションを実際に使うなら、6件の要求を1件にでき、全体として有利な場合があります。ただし完全な可変フォントは静的な1ウェイトより単一ファイルが大きいため、1ウェイトしか描画しないなら、かえって遅くなる可能性があります。使う軸と文字へサブセット化し、圧縮されたWOFF2で配信し、ライセンスも確認してください。すべてのフォントメーカーがサブセット化やセルフホスティングを許可しているわけではありません。

そもそものファイルを小さくする

ここまでの対策は、フォントがいつ読み込まれるかを管理します。読み込むもの自体も小さくできます。フォントのサブセット化は使わないグリフを取り除き(ラテン文字だけのサイトにキリル文字やCJKの範囲は不要です)、WOFF2は十分に圧縮された配信形式です。小さいフォントファイルほどoptionalの時間枠内に届きやすく、LCPを妨げにくくなります。サブセット化と圧縮はfont-displayやpreloadを補完する手段であり、代替ではありません。

ただし、唯一の「正しい」サブセットや普遍的なバイト削減率はありません。対象ユーザーが実際に必要とする文字体系、文字、ウェイトによって変わります。他人の事例やベンチマークで見た数値は自分のページへの保証ではないため、変更前後のフォント要求を計測してください。

ランキング要因なのか

正確に言えば、フォント読み込みは直接のランキング要因ではありません。影響するCLSとLCPはCore Web Vitalsであり、Googleのページエクスペリエンスシグナルの一部です。そしてページエクスペリエンスは、軽量なタイブレーカーのようなシグナルです。関連性と品質が支配的で、優れたページと劣ったページを逆転させるものではなく、他の点で同程度の結果を比較するときに役立ちます。したがって正直な説明は、font-displayの値だけで順位が上がるからではなく、2つのランキング入力にも影響する本物のユーザー体験問題だからフォント読み込みを直す、となります。

私の見解

AhrefsのCLS とLCP のガイドでたどり着いた優先順位は同じで、今も変わりません。システムフォントを使えるなら使ってください。読み込みがなく、遅延もシフトを起こす変更もありません。カスタムフォントがどうしても必要なら、CLSを最小化する現在の最良の方法は、<link rel="preload">(できるだけ早く取得)とfont-display: optional(短い読み込み時間枠)を組み合わせることです。間に合わなければページはデフォルトフォントを表示し、カスタムフォントはキャッシュされて次回以降に表示されます。プリロード、次にoptional、そして最善はカスタムフォントを使わないこと。これが優先順位のすべてです。

専門家メモを追加

専門家の引用を固定

新しい人物ですか?まず、 /admin/experts/ → 専門家の引用を固定 から未登録プロフィールを作成してください。