ファイルを選択
  1. base64hub
  2. Base64 を学ぶ
  3. Data URI のパフォーマンス
パフォーマンス

Data URI のパフォーマンス:Base64 のインライン化が得になるとき

ファイルを Base64 の Data URI としてインライン化すると、リクエストが 1 つ減る代わりに、バイト数、キャッシュ、ときにはレンダリング時間を犠牲にします。圧縮が入ると計算が変わり、SVG ではその変わり方が異なります。

Data URI のコスト

Data URI はファイルを URL の中に入れる仕組みです:data:image/png;base64,iVBORw0KGgo...。URL を書ける場所ならどこでも使えるので、アイコンを HTML の src 属性や CSS の background-image の中に置けば、ブラウザがそれを別途リクエストすることはありません。

トレードオフは次のとおりです。

  • サイズ。 Base64 はペイロードを元の 4/3、つまり約 33% 大きくします(正確な式は Base64 のサイズ増加にあります)。さらに data:image/png;base64, という接頭辞で 22 バイト増えます。
  • キャッシュ。 インライン化したファイルは、それを含む文書の一部としてしかキャッシュされません。すべての HTML ページにロゴをインライン化すると、ページごとに毎回ダウンロードされます。CSS ファイルにインライン化すればその CSS と一緒にキャッシュされますが、アイコンを 1 つ変えるとスタイルシート全体のキャッシュが無効になります。
  • レンダリング。 レンダリングをブロックする CSS が届くまで、ブラウザは何も描画できません。200 KB のフォントや画像をインライン化したスタイルシートは、そのバイトのダウンロードにかかる時間だけ初回描画を遅らせます。HTML では、インラインのデータがそれ以降のコンテンツをバイトストリームの後ろへ押しやります。
  • 遅延読み込みが効かない。 Data URI を使った <img> に loading="lazy" を付けても何も節約できません。バイトはすでにページと一緒に届いているからです。

その代わりに、リクエストが 1 つ減ります。HTTP/1.1 では、ブラウザがホストごとにごく少数の接続しか開かなかったため、これは大きな意味がありました。HTTP/2 や HTTP/3 では多数のリクエストが 1 つの接続を共有するので、節約は小さくなります。主にリクエストとレスポンスのヘッダー分と、ブラウザが遅れて見つけるファイルについての 1 往復分のレイテンシです。

実測:gzip と Brotli は Base64 をどう変えるか

33% という数字は圧縮していないテキストの場合です。ほとんどの HTML と CSS は gzip か Brotli で配信されるので、重要なのは圧縮後のサイズです。私たちが自分で計測しました。

方法。 サンプルファイルは Debian 13 のパッケージから取りました。16×16 の PNG アイコン(HighContrast アイコンテーマ)、48×48 の RGBA PNG(hicolor テーマ)、2880×1800 の Xfce の青い壁紙 JPEG、それを Pillow で品質 80 に縮小した 800×500 の JPEG、Roboto Slab Regular の WOFF2 フォント、2 つの SVG アイコン(479 バイトの Adwaita シンボリックアイコンと 7.6 KB の GNOME の Debian スワール)です。各ファイルとその Base64(data: 接頭辞を除いたペイロードのみ)を Node.js 24 の zlib で圧縮しました。設定は、事前圧縮した静的アセットでよく使われる gzip レベル 9 と Brotli 品質 11 です。サイズの単位はバイトです。

ファイル元のサイズBase64元 gzipBase64 gzip元 BrotliBase64 Brotli
PNG 16×16281376285306285284
PNG 48×484,5526,0724,5754,6024,5514,589
JPEG 800×5008,19510,9287,4257,1737,1606,908
JPEG 2880×1800197,011262,684182,684183,945180,114181,140
WOFF2 フォント52,53270,04452,53852,76552,53652,690
SVG アイコン479640291427247410
SVG スワール7,61010,1483,1234,3982,5823,823

数値からわかること:

圧縮済みのバイナリでは、増加分がほぼすべて消えます。 PNG、JPEG、WOFF2 のデータはすでに圧縮されていてランダムに近いため、再圧縮してもほとんど縮みません(どちらの PNG も gzip でわずかに大きくなりました)。ランダムなバイト列の Base64 は 64 種類の記号をそれぞれ 8 ビットで表しますが、ハフマン符号化によって 1 記号あたり約 6 ビットに戻ります。圧縮した Base64 は圧縮したバイナリの 96〜107%、元のファイルの 84〜109% に収まりました。今回の JPEG はそれ自体がわずかに圧縮できたため、Base64 版が gzip した JPEG より少し小さくなることさえありました。とはいえ、当てにできるほどの差ではありません。16×16 の小さな PNG では gzip の固定 18 バイトのヘッダーとトレーラーが大きな割合を占めますが、Brotli は枠組みが軽いのでその差が消えました。

SVG は悪化します。 SVG は繰り返しの多いテキストで、圧縮と非常に相性がよく、スワールは gzip で 7,610 バイトから 3,123 バイトに縮みました。Base64 は 3 バイトのグループを、アラインメントによって値が変わる 4 文字に組み替えるため、同じ部分文字列が 2 度目には同じに見えなくなります。gzip した Base64 の SVG は gzip した SVG より 41〜47% 大きく、Brotli では 48〜66% 大きくなりました。

URL エンコードした SVG と Base64

SVG はもともとテキストなので、Base64 はまったく必要ありません。URL エンコードした Data URI なら読みやすさも保てます。

.flag {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 0v16h2V8h3l1 2h5V2h-4L9 0z'/%3E%3C/svg%3E");
}

最低限エスケープが必要なのは %、#(URL のフラグメントの始まりになるため)、<、> と、属性を囲む引用符です。SVG 内の二重引用符を一重引用符に置き換えれば、引用符のエスケープは不要になります。ASCII 以外の文字は UTF-8 としてパーセントエンコードする必要があります。

同じ 2 つの SVG を同じ方法で計測した結果です。

SVGエンコードサイズgzipBrotli
アイコンBase64640427410
アイコン最小限の URL エンコード493293253
アイコンencodeURIComponent723326279
スワールBase6410,1484,3983,823
スワール最小限の URL エンコード7,1793,0572,549
スワールencodeURIComponent11,5943,3192,750

「最小限」とは、空白をまとめ、二重引用符を一重引用符に変え、%、#、<、> だけをエスケープしたものです。圧縮後は元の SVG とほぼ同じサイズになり、スワールは空白をまとめたぶんだけむしろ少し小さくなりました。完全な encodeURIComponent は圧縮前こそ大きいものの、圧縮すれば Base64 よりはるかに小さくなります。SVG を Data URI に変換ツールは両方の形式を並べて出力します。

それでも SVG に Base64 を使う意味があるのは、ツールや API が Base64 しか受け付けない場合や、エスケープを気にしたくない文字が SVG に含まれている場合です。

Content Security Policy と data:

Content Security Policy では、'self' に data: URL は含まれません。許可したいディレクティブごとにスキームを列挙する必要があります。

Content-Security-Policy: default-src 'self'; img-src 'self' data:; font-src 'self' data:

img-src と font-src で data: を許可するのは一般的で、リスクも低めです。script-src、default-src、object-src で許可するのは話が別です。マークアップを注入できる攻撃者が外部ホストなしでスクリプトを送り込めるようになり、CSP の意義の多くが失われます。インライン画像がポリシーに引っかかる場合は、img-src だけを広げてください。

また、ブラウザはリンク、リダイレクト、スクリプトのいずれであっても、トップレベルのウィンドウを data: URL に遷移させることをブロックします。そのため Data URI はページに埋め込むことはできても、トップレベルのリンクとして開くことはできません。

インライン化する価値があるとき

たいていは価値がある:

  • 小さなアイコンや UI 画像。 目安としておよそ 1〜2 KB 未満のもの、特に初回描画に必要なものです。リクエストのオーバーヘッドがサイズに対して大きな割合を占め、Base64 のコストは圧縮でほとんど消えます。
  • 単一ファイルの HTML。 オフラインのレポート、エクスポートしたドキュメント、テストカバレッジのページ、ディスクから開いても動く必要がある HTML メールのプレビューなどです。
  • クリティカル CSS。 どのみち HTML にインライン化する CSS なら、小さな背景画像を一緒に運べます。
  • 生成されたコンテンツ。 スクリプトで PNG に描画したグラフを HTML レポートに埋め込む場合などです。

たいていは価値がない:

  • 写真や数 KB を超えるもの。長いキャッシュ期間を設定した別ファイルにしたほうが有利です。
  • 多くのページで共有する画像。HTML のレスポンスのたびに再ダウンロードされてしまいます。
  • Web フォント。WOFF2 ファイルは通常数十 KB あり(上で計測したものは 52 KB)、CSS にインライン化するとスタイルシートがレンダリングをブロックする時間が長くなります。別ファイルのフォントならキャッシュもプリロードもできます。
  • スクロールしないと見えない位置のもの。loading="lazy" を付けた別ファイルなら、必要になるまでコストはかかりません。

メールは別の話です。data: 画像への対応はメールクライアントによってまちまちなので、受信者が実際に使っているクライアントでテストしてください。HTML メールに画像を埋め込む確実な方法は、cid: で参照するインラインの MIME 添付です。これも Base64 ですが、MIME 構造の中にあります。メールでの Base64 をご覧ください。

画像から Data URI を作成

PNG、JPEG、WebP、SVG をドロップすると、Data URI とそのサイズが表示されます。ファイルはブラウザの外に出ません。

画像を Base64 に変換

よくある質問

Base64 の画像は通常の画像より遅いですか?

本質的に遅いわけではありません。小さなインライン画像はリクエストを減らせますが、大きな画像は HTML や CSS を肥大化させ、個別にキャッシュできず、レンダリングを遅らせることがあります。数 KB 未満ならインライン化は有効で、それ以上では逆効果です。

gzip で Base64 の 33% の増加はなくなりますか?

PNG、JPEG、WOFF2 ではほぼ完全になくなります。私たちの計測では、gzip した Base64 はバイナリファイルとの差が数 % 以内に収まりました。SVG などのテキストではなくならず、gzip した Base64 の SVG は gzip した SVG より 41〜47% 大きくなりました。

インライン SVG には URL エンコードと Base64 のどちらがよいですか?

URL エンコードです。読みやすく、編集でき、元の SVG とほぼ同じくらいに圧縮されます。少なくとも #、%、<、> をエスケープし、属性を二重引用符で囲む場合は SVG 内で一重引用符を使ってください。

Data URI の画像が Content Security Policy でブロックされるのはなぜですか?

CSP は data: を別のスキームとして扱い、'self' には含まれません。img-src(フォントなら font-src)に data: を追加してください。script-src や default-src には追加しないでください。

続けて読む