Data URI で画像を埋め込む技術と注意点 - Base64 エンコードの仕組みと最適な使いどころ
Data URI スキームとは - 画像をテキストとして埋め込む仕組み
Data URI (Data URL) スキームは、外部ファイルへの参照ではなく、データそのものを URL として直接記述する方式です。RFC 2397 で定義されており、画像、フォント、CSS、JavaScript など任意のデータを HTML や CSS に直接埋め込むことができます。
Data URI の構文:
data:[<mediatype>][;base64],<data>
画像の Data URI 例:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==
構成要素:
- data:: スキーム識別子。ブラウザにこれが Data URI であることを伝える
- image/png: MIME タイプ。画像のフォーマットを指定する (image/jpeg, image/svg+xml, image/webp など)
- ;base64: エンコード方式の指定。バイナリデータを Base64 でテキスト化していることを示す
- ,iVBOR...: Base64 エンコードされた画像データ本体
Data URI を使用する主な動機は HTTP リクエスト数の削減です。小さなアイコンやプレースホルダー画像を個別のファイルとして配信すると、各画像に対して HTTP リクエストが発生します。Data URI で埋め込めば、HTML や CSS のダウンロード 1 回で画像データも同時に取得でき、追加のリクエストが不要になります。ただし HTTP/2 以降ではリクエストの多重化が可能なため、この利点は大幅に薄れています。
Base64 エンコードの仕組みとサイズへの影響
Base64 は、バイナリデータを ASCII 文字列に変換するエンコード方式です。6 ビットずつ区切って 64 種類の文字 (A-Z, a-z, 0-9, +, /) に変換するため、元データの約 33% サイズが増加します。
Base64 エンコードの仕組み:
- 入力: 任意のバイナリデータ (画像ファイルのバイト列)
- 処理: 3 バイト (24 ビット) を 4 文字 (各 6 ビット) に変換する。入力が 3 の倍数でない場合は
=でパディング - 出力: A-Z (26 文字) + a-z (26 文字) + 0-9 (10 文字) + +/ (2 文字) = 64 文字で構成されるテキスト
- サイズ増加: 元データの 4/3 倍 (約 33% 増)。1KB の画像は約 1.37KB の Base64 文字列になる
コマンドラインでの変換:
base64 -i icon.png -o icon.txt (macOS) / base64 icon.png > icon.txt (Linux)
JavaScript での変換:
const toDataUri = (file) => new Promise((resolve) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.readAsDataURL(file); });
サイズ増加の実際の影響:
- 1KB のアイコン → 1.37KB (増加量 0.37KB、許容範囲)
- 10KB のサムネイル → 13.7KB (増加量 3.7KB、要検討)
- 100KB の写真 → 137KB (増加量 37KB、非推奨)
- 1MB の画像 → 1.37MB (増加量 370KB、絶対に避ける)
さらに、Base64 文字列は gzip 圧縮の効率が悪い (バイナリデータより圧縮率が低い) ため、実質的なサイズ増加は 33% 以上になる場合があります。HTML/CSS ファイル全体の gzip 圧縮率も低下させるため、大きな画像の埋め込みは二重にデメリットがあります。
HTML と CSS での Data URI の使い方
Data URI は HTML の <img> タグと CSS の background-image プロパティの両方で使用できます。それぞれの記述方法と使い分けを解説します。
HTML での使用:
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHJlY3Qgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSIjY2NjIi8+PC9zdmc+" alt="placeholder" width="16" height="16" />
CSS での使用:
.icon-check { background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"%3E%3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/%3E%3C/svg%3E'); }
SVG の場合の最適化:
- Base64 不要: SVG はテキストベースなので、Base64 エンコードせずに URL エンコード (パーセントエンコーディング) で直接埋め込める。サイズ増加が Base64 の 33% に対して 10-15% 程度に抑えられる
- 最小限のエスケープ:
<→%3C、>→%3E、#→%23、"→'(シングルクォートに置換) の最小限のエスケープで動作する - CSS カスタムプロパティとの組み合わせ:
--icon-colorを SVG 内で参照することはできないが、currentColorを使えば CSS のcolorプロパティで SVG の色を制御できる
CSS Sprites との比較: 複数の小さなアイコンを管理する場合、CSS Sprites (1 枚の画像にまとめて background-position で切り出す) と Data URI のどちらが適切かはケースバイケースです。HTTP/2 環境では個別ファイルのままでも問題ないため、保守性を優先して個別 SVG ファイル + <use> 参照が現代のベストプラクティスです。
パフォーマンスへの影響 - Data URI のメリットとデメリット
Data URI のパフォーマンス特性を正確に理解し、適切な場面でのみ使用することが重要です。HTTP/1.1 時代のベストプラクティスが HTTP/2 以降では逆効果になるケースがあります。
メリット:
- HTTP リクエスト削減: 画像ごとの個別リクエストが不要になる。HTTP/1.1 では同時接続数制限 (6 本) があるため、小さな画像が多いページで効果的
- レンダリングブロック回避: CSS 内に埋め込んだ画像は、CSS のダウンロード完了と同時に利用可能になる。追加の画像リクエストを待つ必要がない
- オフライン対応: HTML ファイル 1 つで画像を含む完全なページを表現できる。メール HTML やオフラインドキュメントに有用
デメリット:
- キャッシュ不可: Data URI は HTML/CSS ファイルの一部としてキャッシュされる。画像だけを個別にキャッシュできないため、HTML が変更されるたびに画像データも再ダウンロードされる
- サイズ増加: Base64 で 33% 増加し、gzip 圧縮効率も低下する。HTML/CSS ファイル全体のサイズが膨張する
- パース負荷: ブラウザは Base64 文字列をデコードしてからレンダリングする。大きな Data URI は HTML パーサーの処理を遅延させる
- HTTP/2 での無効化: HTTP/2 はリクエストの多重化をサポートするため、リクエスト数削減のメリットがほぼ消失する
- CSP (Content Security Policy) との衝突:
img-src 'self'ポリシーでは Data URI がブロックされる。data:を明示的に許可する必要がある
Core Web Vitals への影響: 大きな Data URI は HTML のダウンロードサイズを増加させ、FCP (First Contentful Paint) を遅延させます。特に LCP 要素に Data URI を使用すると、LCP スコアが悪化します。
適切なユースケースと避けるべきケース
Data URI を使うべき場面と避けるべき場面を明確に整理します。判断基準は「画像サイズ」「再利用頻度」「キャッシュの重要性」の 3 軸です。
Data URI が適切なケース:
- 1KB 未満の極小アイコン: チェックマーク、矢印、閉じるボタンなど。Base64 増加分を含めても 1.5KB 未満であれば、HTTP リクエスト 1 回のオーバーヘッド (DNS + TCP + TLS で 50-200ms) を削減する価値がある
- LQIP (Low Quality Image Placeholder): 画像の遅延読み込み時に表示する極小のぼかしプレースホルダー。20-50 バイトの 1x1 ピクセル画像や、200-500 バイトの超低解像度サムネイル
- CSS 内の装飾的 SVG: ボーダー装飾、背景パターン、カスタムリストマーカーなど。SVG を URL エンコードで埋め込むとサイズ増加が最小限
- メール HTML: メールクライアントは外部画像をブロックすることが多いため、Data URI で埋め込むと確実に表示される (ただしメールクライアントの対応状況に依存)
- 単一ファイルのドキュメント: オフラインで閲覧する HTML レポートや、外部依存なしで動作する必要があるページ
Data URI を避けるべきケース:
- 10KB 以上の画像: サイズ増加とキャッシュ不可のデメリットが大きすぎる
- 複数ページで共有する画像: 個別ファイルならブラウザキャッシュで 2 ページ目以降はダウンロード不要。Data URI は毎回ダウンロードされる
- 頻繁に更新される HTML 内の画像: HTML が変わるたびに画像データも再ダウンロードされる
- LCP (Largest Contentful Paint) 対象の画像: メインビジュアルやヒーロー画像。HTML サイズの増加が LCP を悪化させる
- レスポンシブ画像:
srcsetや<picture>による出し分けが不可能になる
ビルドツールでの自動化と実装パターン
Data URI の生成をビルドパイプラインに組み込み、手動でのエンコード作業を排除する実装パターンを紹介します。
webpack での自動インライン化:
- asset/inline: webpack 5 の Asset Modules で、指定サイズ以下の画像を自動的に Data URI に変換する
- 設定例:
{ test: /\.(png|svg)$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 4096 } } } - 4KB 以下の画像は自動的に Data URI に、それ以上は通常のファイルとして出力される
Vite での自動インライン化:
- Vite はデフォルトで 4KB 未満のアセットを自動的にインライン化する
- 閾値の変更:
build: { assetsInlineLimit: 2048 }(2KB に変更) - 特定ファイルの強制インライン:
import icon from './icon.svg?inline'
PostCSS プラグイン:
- postcss-url: CSS 内の
url()参照を自動的に Data URI に変換する。postcss-url({ url: 'inline', maxSize: 4 })で 4KB 以下を自動インライン化
Next.js での LQIP 生成:
next/imageのplaceholder="blur"は自動的に LQIP を Data URI として生成する- カスタム LQIP:
plaiceholderライブラリで任意の画像から超低解像度の Base64 プレースホルダーを生成
推奨する閾値設定:
- SVG アイコン: 2KB 以下をインライン化 (URL エンコード方式)
- ラスター画像: 1KB 以下をインライン化 (Base64 方式)
- LQIP: サイズに関係なく常にインライン化 (数十バイト〜数百バイト)
CI/CD での検証: ビルド後の HTML/CSS ファイルサイズを監視し、Data URI の過剰な埋め込みによるサイズ膨張を検出するチェックを組み込みます。bundlesize や Lighthouse CI で閾値を設定し、超過時にアラートを発報します。