Google Fontsで学ぶWebフォントの基本 第1回 Webフォントが表示されるまで

まずはGoogle Fontsを例にWebフォントがどのように読み込まれ、ブラウザのページ上に表示されるのか、その過程を追ってみます。

発行

著者 藤田 智朗 フロントエンド・エンジニア
Google Fontsで学ぶWebフォントの基本 シリーズの記事一覧

はじめに

見た目が重視されるWebサイトやWebアプリでは、Webフォントが気軽に使われるようになりました。特に最近はフレームワークやツールがフォントの読み込みや最適化を自動で行ってくれるため、Webフォントのことをよく理解せずに利用されているというケースもあります。

理解せずに使っていると、次のような状態になっていることがあります。

  • フレームワークがフォントを最適化してくれているはずだが、何をしているのかわからない
  • preconnectcrossoriginの指定がなぜ必要なのか説明できない
  • DevToolsを見ても、どのフォントがいつ読み込まれているのか把握できない
  • 日本語フォントでwoff2がたくさん読み込まれる理由がわからない

なんとなく使っているので、問題が起きたときに対処できるか、不安を感じている人も少なくないのではないでしょうか。

このシリーズでは、そうした「なんとなく使われがちなWebフォント」を一度立ち止まって見直し、ブラウザやフレームワークが実際に何をしているのかを、仕組みから整理して解説していきます。Webフォントサービスとしては多くの方が利用しているGoogle Fontsを題材に進めていきますが、@font-faceやCORSといった基本的な仕組みは、他のフォントサービスやセルフホスティングにも共通します。

まずはフレームワークを使わない素の構成から始め、その上でフレームワークがどこを自動化し、どこをブラックボックスにしているのかなどを解説します。フレームワークの例としてはNext.jsを取り上げますが、Astroも2026年1月現在は実験的機能ながら同様のフォント最適化機能を提供しており、基本的な考え方は共通しています。

このシリーズを通して「Webフォントが重い」「日本語フォントは扱いが難しい」といった感覚的な判断ではなく、DevToolsのネットワークパネルやビルド成果物を見て、挙動を説明・判断できる状態を目指します。

Webフォントの最小構成

Webフォントを使うために必要な要素は、実はそれほど多くありません。フレームワークを使わない場合、最低限必要なのは次の2つだけです。

  1. フォントを読み込むための指定
  2. そのフォントを使うという指定

具体的には、次のようなコードになります。

HTML:フォントを読み込むための指定

<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700">

CSS:そのフォントを使うという指定

body {
  /* 実際の運用ではbodyへのWebフォントの指定は避けることを推奨(後述) */
  font-family: "Noto Sans JP", sans-serif;
}

これだけで、ブラウザはWebフォントを表示できます。

なお、この例では説明を簡潔にするためにbody全体にフォントを指定していますが、実際の運用では日本語Webフォントをbodyに指定するのは避けたほうがよいでしょう。ページ内のすべてのテキストがフォントの適用対象となり、使用される文字数が増えるほどフォントの転送量も増大するためです。見出しやキャッチコピーなど、限定的な要素にのみ適用するのが一般的です。

Webフォントを使用する場合、多くのケースでpreconnect@font-facefont-display: swapといった指定をしますが、これらは後から追加する最適化のための対応であり、Webフォントの表示のための成立条件ではありません。preconnectfont-displayについては次回以降で詳しく解説します。

Google Fontsの仕組み

Google Fontsを例にWebフォントがブラウザに表示されるまでの過程を追ってみましょう。

ブラウザの処理フロー

HTMLの<link rel="stylesheet">を見つけたとき、ブラウザは次のように動作します。

  1. fonts.googleapis.comからCSSファイルを取得
  2. そのCSSの中に含まれる@font-faceを解析する
  3. @font-faceに書かれたURLを元に、実際のフォントファイル(woff2)を取得する
  4. フォントが利用可能になった時点で描画に使用する

ここでポイントになるのが、フォントファイルをHTMLから直接読み込んでいるわけではなく、CSSに書かれた@font-faceを介して読み込んでいるという点です。HTML → CSS → フォントファイルという二段階構造になっています。

@font-faceはどんな役割を担っているのでしょうか?

@font-faceの役割

実際にfonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700にアクセスしてみましょう。@font-faceがズラッと並んだCSSファイルが表示されるはずです。

Google FontsのCSS

@font-face {
  font-family: 'Noto Sans JP';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/...) format('woff2');
  unicode-range: U+0000-00FF;
  /* 略 */
}

このような定義を見つけたとき、ブラウザは次のように解釈します。

  • このフォントファミリー名は「Noto Sans JP」である
  • 必要になったら、srcに指定されたURLからフォントを取得すればよい
  • unicode-rangeで指定された文字範囲に対してこのフォントを使う

ここで押さえておきたいのは、@font-face定義が読み込まれた瞬間にフォントがダウンロードされるわけではないという点です。実際には、そのページ内でそのフォントが使われていて、かつ該当する太さ・スタイルが必要になったときに、はじめてフォントファイルの取得が始まります。

つまり、CSSでfont-family: "Noto Sans JP"と指定していても、そのページに文字がない場合や、該当する太さが使われていない場合は、フォントが取得されないこともあります。

日本語フォントの分割配信(unicode-range)

Noto Sans JPのような日本語フォントでは、@font-faceが次のように分割されています。

  • 英数字向け(latin、latin-ext)
  • 日本語向け(japanese):100以上のサブセットに分割
  • 太さごと(400 / 700 など)

日本語のサブセットは、使用頻度とUnicodeコードポイントの範囲に基づいて分割されています。Google Fontsの公式ブログによると、高頻度の3,000文字を20分割、残りの文字をUnicodeコードポイント順に100分割することで、効率的な配信を実現しています。

これはGoogle Fontsの次のような設計思想によるものです。

  • すべての文字を一括で送らない
  • 実際に使われた範囲だけを配信する
  • ブラウザ側に判断を委ねる

その結果として、英数字だけのページと日本語を含むページでは、取得される.woff2の数もサイズも変わります。ページ内容によってフォントの通信内容が変わるという挙動になるのです。

DevToolsで確認する

ここまで説明してきた仕組みを、実際にChromeのDevToolsで確認してみましょう。次のデモページを使って、Webフォントがどのように読み込まれているかを観察します。

https://codegrid.github.io/2026-webfont/1/index.html にアクセスして実際にDevToolsを開いて確認してみてください。

このデモでは、システムフォントとWebフォント(Noto Sans JP)を並べて表示しています。見た目の違いだけでなく、裏側でどのような通信が行われているかを確認してみましょう。

ネットワークパネルでの確認手順

  1. デモページを開いてDevToolsを開く(ページ上の要素を右クリックして「検証」を選択)
  2. ネットワークパネルを選択する
  3. ページをリロードする
  4. フィルタで「CSS」を選択するとCSSファイルへのリクエストが確認できる
  5. フィルタで「Font」を選択するとwoff2ファイルへのリクエストが確認できる

CSSの取得(fonts.googleapis.com)

最初にfonts.googleapis.comへのリクエストが発生します。これは<link rel="stylesheet">で指定したURLへのアクセスで、@font-faceの定義が含まれたCSSファイルを取得しています。

CSSのイニシエータ(Initiator)を見るとindex.html:10となっており、index.htmlから、このCSSファイルがリクエストされていることがわかります。

補足:「イニシエータ(Initiator)」とは

イニシエータとは、あるネットワークリクエストが何をきっかけに発生したか、リクエスト元を示すものです。

フォントファイルの取得(fonts.gstatic.com)

CSSの取得後、fonts.gstatic.comから.woff2ファイルが取得されます。ここで注目したいのは、取得されるファイルの数です。

デモページでは「あいうえお 漢字 ABC 123」という短いテキストを表示しているだけですが、複数の.woff2ファイルが取得されているはずです。これは、ページ内で使われている文字が複数のサブセットにまたがっているためです。

また、デモではfont-weight: 400font-weight: 700の両方を使っているため、それぞれの太さに対応するサブセットが別々に取得されます。

なお、ネットワークパネルでwoff2ファイルを選択してPreviewタブを開いても、実際のフォントの内容を見ることはできません。これは、woff2ファイルがバイナリ形式で圧縮されているためです。フォントファイルの中身を確認したい場合は専用のツールが必要になります。woff2の内部構造とその確認方法については、このシリーズの今後の記事で詳しく解説する予定です。

イニシエータ列で読み込み元を確認する

ネットワークパネルの「イニシエータ」列を見ると、各リソースがどこから読み込まれたかを確認できます。

  • CSSファイル → HTMLの<link>タグから読み込まれている
  • woff2ファイル → CSSファイルから読み込まれている

これにより、HTML → CSS → フォントファイルという二段階構造を視覚的に確認できます。

キャッシュの影響に注意

2回目以降のアクセスでは、フォントファイルがブラウザにキャッシュされている場合があります。キャッシュなしの状態で確認したい場合は、DevToolsのネットワークパネルで「キャッシュを無効化」にチェックを入れてからリロードしてください。

まとめ

今回は、Webフォントを使うための最低限の指定について解説しました。<link rel="stylesheet">でGoogle FontsのCSSを読み込み、font-familyでフォントを指定するだけで、Webフォントは表示できます。

ただし、Google Fontsのサイトで「Get Embed code」で提供されているコードを見ると、次のようなpreconnectを使った指定がされています。

Google Fontsが提供する埋め込み用コードの一例

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700" rel="stylesheet">

前述しましたが、これは読み込みを最適化するための指定であり、Webフォントを表示するための必須要件ではありません。次回は、このpreconnectと2行目のcrossorigin属性がどのような役割を果たしているのかについて解説します。