[ガイド] 10 分で読了

[ガイド] データ URI を理解する: data:image/png;base64,... の解説

data: URI スキームの実践的なガイドです。data:image/png;base64,iVBORw0KGgo... の各部分が実際に何を意味するのか、インライン化が正しい選択になるのはいつか、そしてどのような場面で逆効果になるのかを解説します。

2026 年 4 月 | fundamentals

// data: URI とは実際に何なのか

data: URI は https: や file: と同じ URI スキームの 1 つで、リモートの場所を指し示す代わりに、リソースそのものを URI 文字列に直接埋め込む仕組みです。RFC 2397 で定義されており、小さなバイナリやテキストデータの塊をあたかも URL であるかのように扱えます。

実際によく目にするのは画像用の形式で、data:image/png;base64,iVBORw0KGgoAAAANSUhEUg... のような書き方です。ブラウザはこの URL を見ると MIME タイプを読み取り、Base64 ペイロードをバイト列にデコードし直して画像をレンダリングします。HTTP リクエストは一切発生しません。

// 構文を 1 つずつ分解する

完全な構文は data:[<mediatype>][;base64],<data> です。3 つの省略可能な要素と、必須の区切り文字 (カンマ) で構成されます。実例で分解してみましょう。

data:image/png;base64,iVBORw0KGgoAAAANSUhEUg... を左から右に読み解きます。
data: — スキーム。「リソースは URL 内にあり、フェッチは不要」とパーサーに伝えます。
image/png — MIME タイプ。レンダラーにペイロードの解釈方法を伝えます。省略すると text/plain;charset=US-ASCII が既定値になります。
;base64 — エンコーディングフラグ。ペイロードが Base64 (RFC 4648) であることを示します。これがない場合、ペイロードは URL エンコードされたテキストとして解釈されます。
, — メタデータとペイロードの間の必須の区切り文字です。
iVBORw0KGgo... — 実際の Base64 エンコードされた画像のバイト列。

// Anatomy diagram
// data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...
// │    │         │      │
// │    │         │      └── Base64 payload (the actual image bytes)
// │    │         └────────── encoding flag (always 'base64' for binary)
// │    └──────────────────── MIME type (optional, defaults to text/plain)
// └───────────────────────── scheme literal (always 'data:')

// data: URI が存在する理由

通常の画像 URL ではなくデータ URI を使う理由は、主に次の 3 つです。

1. HTTP リクエストを削減する。 ページ内のすべての <img src="..."> はネットワークのラウンドトリップを 1 回ずつ消費します。小さなアイコンやスプライトでは、そのラウンドトリップにかかる時間がバイト列の転送時間より長くなることもあります。画像をデータ URI としてインライン化すれば HTML/CSS にバンドルされるため、リクエスト自体がなくなります。HTTP/1.1 時代には大きな意味がありましたが、HTTP/2 の多重化が普及した現在では効果は小さくなりました。とはいえ、ファーストビュー上のクリティカルなアイコンには今でも有効です。

2. アセットを自己完結させる。 HTML メールは、リモート画像をデフォルトでブロックするクライアント (Gmail、Outlook など) でも表示される必要があります。クライアントへ送信する静的レポートは、オフラインでも会社ロゴを表示しなければなりません。ブックマークレットには、コピー&ペーストでも生き残るアイコンが必要です。Markdown のドキュメント断片には、文書と一緒に持ち運べるスクリーンショットが必要です。これらすべてのケースで画像はドキュメント内部に存在しなければならず、データ URI はそれを実現する手段です。

3. プログラムによる動的生成。 QR コード、グラフ、サインパッドのように、コードがランタイムで画像を生成し、それをサーバーへアップロードして URL を取得することなく表示したい場合があります。canvas.toDataURL('image/png') はデータ URI を直接返してくれるので、それを img.src に代入するだけが最もシンプルなワークフローです。

// 実際に目にする MIME タイプの一覧

MIME タイプの欄には任意のメディアタイプを指定できますが、画像の場合に実際に目にするのは限られた数種類です。

image/png — もっとも一般的。可逆圧縮で透明度をサポート。Base64 は iVBORw0KGgo で始まります。
image/jpeg — 写真とスクリーンショット。非可逆。Base64 は /9j/ で始まります。
image/gif — レガシーおよびアニメーション画像。Base64 は R0lGOD で始まります。
image/webp — モダンで小さなファイル。Base64 は UklGR で始まります。
image/svg+xml — ベクターグラフィックス。SVG はテキストなので、Base64 エンコードする (;base64 付き) ことも、URL エンコードする (なし) こともできます。URL エンコード版の方が通常は小さくなります。
image/x-icon または image/vnd.microsoft.icon — favicon。Base64 は AAABAA で始まります。

<!-- All five types in a single HTML document -->
<img src="data:image/png;base64,iVBORw0KGgo..." alt="PNG inline">
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRg..." alt="JPEG inline">
<img src="data:image/gif;base64,R0lGODlhAQABAA..." alt="GIF inline">
<img src="data:image/webp;base64,UklGRiIAAABXRU..." alt="WebP inline">
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxu..." alt="SVG inline">

// HTML、CSS、JavaScript でのデータ URI の使い方

URL を受け付ける場所であれば、データ URI は通常の URL とまったく同じように動作します。

<!-- HTML <img> tag -->
<img src="data:image/png;base64,iVBORw0KGgo..." width="32" height="32" alt="icon">

<!-- HTML <link> for favicon -->
<link rel="icon" href="data:image/x-icon;base64,AAABAAEAEBA...">

/* CSS background-image */
.button-icon {
  background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxu...');
  background-size: 16px 16px;
}

/* CSS @font-face — yes, fonts work too */
@font-face {
  font-family: 'InlineFont';
  src: url('data:font/woff2;base64,d09GMgAB...') format('woff2');
}

// JavaScript — assign directly to img.src
const img = new Image();
img.src = 'data:image/png;base64,iVBORw0KGgo...';
document.body.appendChild(img);

// JavaScript — fetch a data URI like any other URL
const response = await fetch('data:application/json;base64,eyJrZXkiOiJ2YWx1ZSJ9');
const data = await response.json(); // { key: 'value' }

// Canvas、File、Blob → data: URI への変換

次の 3 つのブラウザ API は、自分で Base64 を扱わなくてもデータ URI を返してくれます。

// 1. Canvas → data URI (instant)
const canvas = document.querySelector('canvas');
const pngDataURI = canvas.toDataURL('image/png');
const jpegDataURI = canvas.toDataURL('image/jpeg', 0.85); // quality 0–1
// pngDataURI === 'data:image/png;base64,iVBORw0KGgo...'

// 2. <input type="file"> → data URI via FileReader
const file = document.querySelector('input[type=file]').files[0];
const reader = new FileReader();
reader.onload = () => {
  // reader.result is the data URI
  document.querySelector('img').src = reader.result;
};
reader.readAsDataURL(file);

// 3. Blob → data URI (manual, but rarely needed — use URL.createObjectURL instead)
const blob = new Blob([bytes], { type: 'image/png' });
const reader2 = new FileReader();
reader2.onload = () => console.log(reader2.result);
reader2.readAsDataURL(blob);
// Or, for in-page display, prefer:
const objectURL = URL.createObjectURL(blob); // 'blob:https://...' — much shorter, no Base64

// 33% のサイズペナルティ (とそれが問題になる場面)

Base64 はバイナリ 3 バイトを 4 つの ASCII 文字に変換するため、エンコード後のペイロードはちょうど 4⌈n/3⌉ バイト、つまり元の約 33% 増しになります。12 KB の PNG はおよそ 16 KB のデータ URI になり、さらに data:image/png;base64, プレフィックスのオーバーヘッドが数バイト加わります。

では、これが問題になるのはいつか。
小さなアセット (4 KB 未満): 33% の肥大化は、節約できる HTTP ラウンドトリップに比べれば誤差です。気軽にインライン化して構いません。
中程度のアセット (4〜50 KB): ケースバイケース。HTTP/2 + キャッシュを使えば、別リクエストの方がインライン版を毎回ロードするよりも通常は速くなります。
大きなアセット (50 KB 以上): ほぼ常にインライン化すべきではありません。データ URI は、それを含むキャッシュ済み HTML ページ、メール、JSON ペイロードのすべてを肥大化させます。別 URL にしましょう。

データ URI にはキャッシュが効きません。HTML に焼き込まれたバイト列の画像をブラウザがキャッシュすることはできないからです。同じ画像が複数のページに登場する場合、すべてのページが満額のコストを支払います。HTTP キャッシュヘッダー付きの通常の <img src="/logo.png"> なら 1 度だけダウンロードしてどこでも再利用できます。

// 直面することになるブラウザ側の制限

ブラウザはデータ URI に対して 1 つの厳格な上限を課しているわけではありませんが、現実的な天井は存在します。

Chrome / Edge / Firefox: <img src> 内のデータ URI は数 MB まで動作します。およそ 32 MB を超えるとタブごとのメモリ圧迫に直面します。
Safari: 歴史的に <a href> (ダウンロードリンク) のデータ URI を約 2 MB に制限していました。<img> ではより大きなサイズでも動作しますが、レンダリングは遅くなります。
Internet Explorer 8: CSS と HTML 内のデータ URI は最大 32 KB。(IE9 以降は制限が撤廃されましたが、モダンブラウザほど高速ではありませんでした。)
HTTP リクエスト行: <form action> やクエリ文字列内のデータ URI は、ブラウザが受け付ける URL 長 (IE で約 2 KB、その他で 8 KB 以上) に制限されます。

これらの制限のいずれかに近づきつつあるなら、そのアセットはインライン化するには大きすぎます。通常の URL に切り替えるか、メモリ上に保持できる Blob にひも付いた短い blob: URL を返してくれる URL.createObjectURL(blob) を使いましょう。

// セキュリティ上の懸念

データ URI はネットワーク不要で便利ですが、コンテンツがサーバーから配信されることを前提とする複数の Web セキュリティ機構をバイパスします。

同一オリジンポリシー。 data: URI は技術的には不透明オリジンとして扱われます (モダンな Chrome/Firefox では)。そのため、データ URI からロードされた JavaScript は親ドキュメントの Cookie、localStorage、DOM に無制限にアクセスすることはできません。これは信頼できないコンテンツのサンドボックス化に有効ですが、同時に、オリジンチェックに依存するトラッキング、解析、CSRF 対策が誤動作する可能性もあります。

data:text/html による XSS。 ユーザーが実行した data:text/html,<script>...</script> URL は、完全な権限を持つページになります。モダンなブラウザはこの理由から data:text/html へのトップレベルナビゲーションをブロックしています (Firefox は 2018 年から、Chrome は 2020 年から)。data: スキームを許容するリダイレクトをユーザー入力の URL に通してはいけません。

Content Security Policy (CSP)。 厳格な CSP は既定で img-srcstyle-src などのデータ URI をブロックします。許可するには、ディレクティブに明示的に data: を含める必要があります (例: img-src 'self' data:)。慎重に判断しましょう。データ URI を許可するということは、ページがインライン化を選んだ任意の画像 (やフォント) も受け入れることを意味します。

ロギングと PII。 データ URI は URL の一部です。URL を記録するロガーや解析ツール (サーバーアクセスログ、ブラウザ履歴、エラートラッカー) はペイロード全体をキャプチャします。データ URI にユーザーのアバター、写真、スクリーンショットが含まれていれば、それはあなたのログに残ります。自分が制御できない URL に PII を入れてはいけません。

// data: URI と blob: URL — どちらを使うべきか

どちらもメモリ上のバイナリデータを、アップロードせずに表示できます。選択はデータがどこに存在する必要があるかで決まります。

data: URI を使うべき場面: データに可搬性が必要なとき。HTML への埋め込み、JSON での送信、データベースへの保存、メールへのコピー&ペーストなど。データそのものが URL になります。

blob: URL を使うべき場面: データがローカルかつ短命なとき。SPA 内のプレビュー、ダウンロードのトリガー、ページを閉じれば消える画像など。URL.createObjectURL(blob) は短い識別子 (blob:https://example.com/12345-abcde) を返し、ブラウザがそれをメモリ上の Blob にマッピングします。データ URI よりはるかに小さく、不要になったら URL.revokeObjectURL(url) で解放できます。

経験則: URL が現在のページの外へ出る必要があるなら data:。ページ内に留まるなら blob:。

// よく遭遇するデバッグの問題

DevTools からコピーすると文字列が切り詰められる。 長いデータ URI を Network タブや Elements パネルからコピーすると、ブラウザは値を省略記号で切り詰めることがしばしばあります。表示されているテキストは完全な URL ではありません。完全な文字列を取得するには、ソースファイル (CSS、HTML、JSON) を直接開くか、コンソールで document.querySelector('img').src を実行します。これは完全な URL を返します。

誤った MIME タイプ。 data:image/jpeg;base64,... とラベル付けされた PNG ペイロードでも、Base64 は MIME を気にしないのでバイトレベルではデコードされます。ただし、ビューアによってはこの不一致を拒否します。実際のフォーマットはマジックバイト (PNG なら iVBORw0KGgo) で決まるので、迷ったときはペイロードを Base64 → 画像デコーダ に貼り付けてください。バイト列から自動検出してくれます。

カンマの欠落。 形式は data:image/png;base64,iVBORw... であって data:image/png;base64;iVBORw... ではありません。セミコロンや区切り文字の欠落は URL 全体を無効にします。

data: URI 内の URL セーフな Base64。 標準のデータ URI は標準の Base64 アルファベット (+ と /) を使います。ペイロードが URL セーフな変種 (RFC 4648 §5、- と _) でエンコードされている場合、埋め込む前に変換し直す必要があります。詳しくは Base64 URL セーフ vs 標準 ガイドを参照してください。

ペイロード内の空白。 一部のプリティプリンタは長い Base64 文字列を 76 桁ごとに改行で折り返します。たいていのブラウザは <img src> でこれを許容しますが、一部 (特に古い WebView) は許容しません。埋め込む前に空白を除去しましょう: str.replace(/\s+/g, '')

// データ URI を使うべきでない場面

50 KB を超える場合。 キャッシュ無効化とダウンロードのコストが、削減できるリクエスト分を上回ります。
繰り返し使われる画像。 複数のページで使われるものは、キャッシュヘッダー付きの通常の URL にすべきです。
公開向けの解析。 トラッキングピクセル、ビーコン、ピクセルベースのアトリビューションは HTTP リクエストが発生しないため動作しません。
SPA で表示するユーザーアップロードコンテンツ。 代わりに URL.createObjectURL(blob) を使いましょう。Base64 の処理を完全に避けられ、メモリ上もおよそ 4 倍小さくなります。
data: を許可しない CSP 厳格環境内。 データ URI を使うためだけに data: を CSP に追加するのはやめましょう。本当に必要かをまず問い直してください。

// 30 秒チートシート

形式: data:[mime-type][;base64],[payload]

もっとも一般的な形: data:image/png;base64,iVBORw0KGgo...

画像にデコードして戻す: Base64 → 画像コンバータ に貼り付けます。

画像をデータ URI にエンコードする: 画像 → Base64 コンバータ を使います。貼り付けてすぐに使える data: URI を生成します。

インラインの目安: 4 KB 未満は常にインライン、4〜50 KB は状況次第、50 KB 超は通常の URL を使用。

ブラウザ API: canvas.toDataURL()FileReader.readAsDataURL()、または自分で文字列を組み立てる方法: 'data:image/png;base64,' + btoa(binaryString)

関連記事: Base64 による画像埋め込み · ブラウザで Base64 画像をプレビューする · Base64 vs Base64URL vs Base32