data URI (data URL とも呼ばれます) は、ファイルの中身を URL の中に直接書き込む仕組みです。小さなアイコンなどを CSS や HTML に埋め込めば、その画像を取りに行くリクエストを 1 回減らせます。スマートフォン向けのサイトが増え始めたころには、表示を速くするための定番の工夫でした。ここでは書き方と、今どこまで使うべきかを整理します。

書き方

data URI は、次の形をしています。

data:[メディアタイプ][;base64],データ

画像のように文字ではないデータは、Base64 (バイナリーのデータを英数字と一部の記号だけで表す符号化の方法) で文字列にして書きます。次の例は、1 ピクセルの青い PNG を埋め込んだものです。実際には、小さなアイコンなどを埋め込みます。

.swatch {
  width: 16px;
  height: 16px;
  /* 1 ピクセルの PNG を、要素いっぱいに引き伸ばして表示する */
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mMwTjvzHwAEmgJlrKcUDwAAAABJRU5ErkJggg==") center / 100% 100%;
}

HTML の img 要素の src にも、同じように書けます。画像を Base64 の文字列にするには、たとえば次のコマンドを使います。

# icon.png を Base64 にして、改行を入れずに出力する
openssl base64 -A -in icon.png

SVG は文字で書かれた形式なので、Base64 にせず、URL として問題になる文字だけを符号化 (パーセントエンコード) して書けます。Base64 にするより短く済み、中身も読めます。

.icon-next {
  width: 16px;
  height: 16px;
  /* < は %3C、> は %3E、# は %23 に置き換える */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5 2l6 6-6 6' fill='none' stroke='%23333' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
}
  • URL 全体を " で囲み、SVG の中の属性の値には ' を使う
  • # は、URL の中では「ここから先はフラグメント」という意味になるので、必ず %23 にする (色の指定で忘れやすい)
  • < と > も、%3C・%3E にしておくと安全

スマートフォンで効果が大きかった理由

HTTP/1.1 では、1 本の接続で基本的に一度に 1 つのリクエストしか扱えず、ブラウザーは同じサーバーへの接続を数本までに限っていました。小さな画像が 20 個あれば、そのたびに要求を送って返事を待つ往復が必要です。

当時のスマートフォンの回線 (3G など) は、通信速度が遅いうえに、1 回の往復にかかる時間 (レイテンシー) が長いのが特徴でした。数百バイトの画像では、データを受け取る時間より、往復を待つ時間のほうがずっと長くなります。data URI で CSS に埋め込めば、この往復をまとめて無くせるため、効果がはっきり表れました。複数の画像を 1 枚にまとめる CSS スプライトが使われていたのも、同じ理由です。

data URI の欠点

  • サイズが大きくなる: Base64 は 3 バイトを 4 文字で表すので、元のファイルの約 3 分の 4 倍 (3 割あまり増える) になる。gzip などで圧縮して送れば差は縮まるが、無くなりはしない
  • 別にキャッシュされない: 画像は、埋め込んだ CSS や HTML の一部としてしか保存されない。HTML に埋め込むと、ページを開くたびに送られる。CSS に埋め込むと、画像を 1 つ差し替えるだけで、CSS 全体を取り直すことになる
  • CSS が大きくなる: CSS はふつう、読み込み終わるまでページの表示を待たせる。そのページで使わない画像まで埋め込むと、最初の表示が遅くなる
  • 同じ画像を何か所にも書くと、そのぶんデータが重複する
  • Content Security Policy (CSP) で画像の読み込み元を制限しているときは、img-src に data: を加えないと表示されない

HTTP/2 以降での使いどころ

HTTP/2 と HTTP/3 では、1 本の接続の中で多くのリクエストを同時に扱えます (多重化)。モバイルの回線のレイテンシーも短くなり、リクエストの数を減らす効果は、HTTP/1.1 のころに比べてずっと小さくなりました。画像は別のファイルにしておくほうが、個別にキャッシュでき、必要なページでだけ読み込め、loading="lazy" で遅らせることもできます。

今、data URI を使うのは、次のような場面に絞るとよいでしょう。

  • CSS の中で使う、ごく小さなアイコン。特に、短く書ける SVG
  • 1 つのファイルで完結させたい HTML (外部のファイルを置けない場面など)
  • ビルドツールに任せる場合。Vite の build.assetsInlineLimit のように、一定の大きさ未満の画像を自動で埋め込む機能を持つものがある

アイコンをたくさん使うなら、SVG をそのまま HTML に書く方法 (インライン SVG) も選択肢です。fill="currentColor" にしておくと、アイコンの色が周りの文字の色に合わせて変わります。

まとめ

  • data URI は data:メディアタイプ;base64,データ の形で、ファイルの中身を URL に埋め込む
  • SVG は Base64 にせず、# などを符号化して書くと短く済む
  • HTTP/1.1 と遅い回線の組み合わせでは、リクエストの往復を減らす効果が大きかった
  • Base64 で約 3 分の 4 倍になる、個別にキャッシュされない、CSS が大きくなる、という欠点がある
  • HTTP/2 以降では、ごく小さな画像に限って使う

参考