[指南] 10 min read

[指南] 認識 Data URI:data:image/png;base64,... 完整解析

data: URI 機制的實用指南 — 解析 data:image/png;base64,iVBORw0KGgo... 每個部分的真正意義、何時內嵌才是正確選擇,以及它何時會反咬你一口。

April 2026 | fundamentals

// data: URI 究竟是什麼

data: URI 是一種 URI 機制 — 就像 https: 或 file: 一樣 — 它直接把資源本身嵌入到 URI 字串中,而非指向遠端位置。它由 RFC 2397 定義,讓你可以把一小段二進位或文字資料當成 URL 來使用。

在實務上最常見的形式是用於圖片:data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...。瀏覽器看到這個 URL,讀取 MIME 類型,把 Base64 內容解碼回位元組,然後渲染圖片 — 完全不需要發出任何 HTTP 請求。

// 逐段解析語法

完整語法為 data:[<mediatype>][;base64],<data>。三個可選部分加上一個必要的分隔符號(逗號)。讓我們拆解一個真實範例:

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

人們選擇 data URI 而非一般圖片網址的三個理由:

1. 省去一次 HTTP 請求。頁面上的每個 <img src="..."> 都要付出一次網路往返成本。對於小圖示或 sprite 而言,往返本身可能比傳輸位元組還久。把圖片以 data URI 內嵌可以將其與 HTML/CSS 一起打包,完全省去請求。這在 HTTP/1.1 時代影響很大;到了 HTTP/2 多工的年代影響較小,但對於首屏關鍵圖示仍有幫助。

2. 讓資產自給自足。HTML 電子郵件需要在預設封鎖遠端圖片的客戶端(Gmail、Outlook)中正常顯示。寄給客戶的靜態報表即使離線也得要顯示公司商標。Bookmarklet 需要一個能在複製貼上後仍存活的圖示。文件片段需要一張能跟著 markdown 一起傳播的截圖。在所有這些情境中,圖片都必須存在於文件內部 — 而 data URI 正是達成這件事的方式。

3. 程式化生成。程式碼在執行階段生成圖片 — QR code、圖表、簽名板 — 而你需要顯示它,卻不想先把它上傳到伺服器再拿回網址。canvas.toDataURL('image/png') 直接給你一個 data 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 編碼的 SVG 通常較小。
image/x-iconimage/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 中使用 data: URI

任何接受 URL 的地方,data URI 都能以同樣方式運作。

<!-- 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

三種瀏覽器 API 不需要你自己做 Base64,就能直接給你 data 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 的 data URI,再加上 data:image/png;base64, 前綴的少許額外負擔。

這何時會有影響?
極小資產(4 KB 以下):33% 的膨脹相較於省下的 HTTP 往返微不足道。儘管內嵌。
中型資產(4–50 KB):視情況而定。在 HTTP/2 加上快取的環境下,獨立請求通常比每次載入頁面都重新下載內嵌版本來得快。
大型資產(50 KB 以上):幾乎絕不要內嵌。data URI 會撐大每個被快取的 HTML 頁面、每封電子郵件、每個包含它的 JSON 內容。請改用獨立網址。

data URI 沒有快取可言 — 瀏覽器無法快取一張位元組已經烤進 HTML 的圖片。如果同一張圖出現在多個頁面,每個頁面都得付出全額代價。一個帶有 HTTP 快取標頭的普通 <img src="/logo.png"> 只下載一次就能在各處重複使用。

// 你會碰到的瀏覽器限制

瀏覽器並未對 data URI 設下單一硬性上限,但實務上仍存在天花板:

Chrome / Edge / Firefox:<img src> 中的 data URI 可以運作到數 MB。超過大約 32 MB 後,你會碰到單個分頁的記憶體壓力。
Safari:歷史上對 <a href>(下載連結)中的 data URI 設有約 2 MB 上限。<img> 可支援更大尺寸,但渲染速度會變慢。
Internet Explorer 8:CSS 與 HTML 中的 data URI 上限為 32 KB。(IE9+ 解除了限制,但速度始終比不上現代瀏覽器。)
HTTP 請求行:<form action> 或查詢字串中的 data URI 受限於瀏覽器接受的網址長度(IE 約 2 KB,其他瀏覽器 8 KB 以上)。

如果你發現自己快碰到任何一個上限,代表這個資產太大了,不適合內嵌。改用一般網址,或使用 URL.createObjectURL(blob),它會給你一個短短的 blob: 網址,背後對應一個你能保留在記憶體中的 blob。

// 安全性考量

data URI 不需網路,這很方便 — 但它會繞過幾項假設內容來自伺服器的網頁安全機制。

同源政策。data: URI 在現代 Chrome/Firefox 中技術上被視為不透明來源(opaque origin),所以從 data URI 載入的 JavaScript 無法以無限制的方式存取父文件的 cookie、localStorage 或 DOM。這對於沙盒化不受信任的內容是好事,但這也代表依賴來源檢查的追蹤、分析與 CSRF 防護可能會失常。

透過 data:text/html 進行 XSS。若使用者執行 data:text/html,<script>...</script> 這樣的網址,它會成為一個擁有完整權限的頁面。基於這個原因,現代瀏覽器封鎖了對 data:text/html 的頂層導覽(Firefox 自 2018 年起,Chrome 自 2020 年起)。不要讓使用者提供的網址透過任何允許 data: 機制的轉址流程。

內容安全政策(CSP)。預設情況下,嚴格的 CSP 會封鎖 img-srcstyle-src 等來源中的 data URI。要允許它們,你必須明確在指令中加入 data:(例如 img-src 'self' data:)。請審慎處理 — 一旦允許 data URI,你也接受了頁面選擇內嵌的任何圖片(或字型)。

日誌與個資。data URI 是網址的一部分。任何擷取網址的記錄器或分析工具(伺服器存取記錄、瀏覽器歷史紀錄、錯誤追蹤工具)都會擷取整個內容。如果你的 data URI 包含使用者的頭像、照片或截圖,這些資料就會出現在你的日誌中。不要把個資放在你無法掌控的網址裡。

// data: URI vs. blob: URL — 該用哪個?

兩者都能讓你顯示記憶體中的二進位資料而不必上傳。選擇取決於資料需要存放在哪裡。

使用 data: URI 的時機:當資料需要可攜時 — 嵌入 HTML、放進 JSON 傳送、存入資料庫、複製貼上到電子郵件中。資料就是網址。

使用 blob: URL 的時機:當資料是本地且短暫的時候 — 在單頁應用中的預覽、觸發下載、頁面關閉就消失的圖片。URL.createObjectURL(blob) 給你一個短的識別字(blob:https://example.com/12345-abcde),瀏覽器會將其對應回記憶體中的 blob。比 data URI 短得多,而且使用完畢後可以用 URL.revokeObjectURL(url) 撤銷它。

通用法則:如果網址需要離開當前頁面,用 data:。如果它留在頁面內,用 blob:。

// 常見的除錯問題

從 DevTools 複製到的字串被截斷。當你從網路面板或元素面板複製一個冗長的 data URI 時,瀏覽器經常會用省略號截斷數值。顯示的文字並非完整網址。要取得完整字串,請直接打開原始檔案(CSS、HTML、JSON),或在主控台中使用 document.querySelector('img').src — 這會回傳完整的網址。

MIME 類型錯誤。標示為 data:image/jpeg;base64,... 的 PNG 內容仍然能在位元組層級解碼 — Base64 不在乎 MIME — 但有些檢視器會拒絕這種不一致。實際格式是由魔術位元組決定(PNG 是 iVBORw0KGgo),所以有疑問時,請把內容貼到我們的 Base64 轉圖片解碼器;它會從位元組自動偵測。

少了逗號。格式是 data:image/png;base64,iVBORw...,而不是 data:image/png;base64;iVBORw...。分號或缺少分隔符號會讓整個網址無效。

data: URI 中出現 URL 安全的 Base64。標準 data: URI 使用標準 Base64 字母表(含 + 與 /)。如果你的內容是用 URL 安全變體(RFC 4648 §5,使用 - 與 _)編碼的,你必須在嵌入前先轉換回去 — 請參考我們的 Base64 URL 安全 vs 標準 指南。

內容中夾雜空白字元。有些 pretty printer 會在 76 欄處用換行包覆冗長的 Base64 字串。大多數瀏覽器在 <img src> 中容忍這種情況,但少數(尤其是較舊的 WebView)無法接受。嵌入前請去除空白:str.replace(/\s+/g, '')

// 何時不該使用 data: URI

超過 50 KB。快取失效加上下載成本超過了省下請求的好處。
重複出現的圖片。任何在多個頁面使用的圖片都應該是帶有快取標頭的一般網址。
對外的分析工具。追蹤像素、信標,以及以像素為基礎的歸因都會失靈,因為根本沒有 HTTP 請求可記錄。
顯示在 SPA 中的使用者上傳內容。請改用 URL.createObjectURL(blob) — 它完全省去 Base64 工作,記憶體佔用約小 4 倍。
在不允許 data: 的嚴格 CSP 環境中。不要為了使用 data URI 就把 data: 加進你的 CSP;先問問自己是否真的需要。

// 30 秒速查表

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

最常見形式:data:image/png;base64,iVBORw0KGgo...

把它解碼回圖片:貼到我們的 Base64 轉圖片轉換器

把圖片編碼為 data URI:使用我們的 圖片轉 Base64 轉換器 — 它會產生完整的 data: URI 供你貼上。

內嵌預算:< 4 KB 一律內嵌,4–50 KB 視情況,> 50 KB 改用一般網址。

瀏覽器 API:canvas.toDataURL()FileReader.readAsDataURL(),或自己組字串:'data:image/png;base64,' + btoa(binaryString)

延伸閱讀:Base64 圖片嵌入 · 在瀏覽器中預覽 Base64 圖片 · Base64 vs Base64URL vs Base32