Data URIs

Embedding resources directly in URLs

What is a Data URI?

A data URI (also called data URL) embeds the resource content directly in the URL itself, rather than pointing to an external file. Instead of telling the browser where to find data, you give it the data directly.

data:[mediatype][;base64],[data]

A data URI consists of:

  1. Scheme: Always data:
  2. Media type: The MIME type (e.g., image/png, text/html)
  3. Encoding: Optional ;base64 if data is base64-encoded
  4. Data: The actual content, after the comma

Basic Syntax and Examples

Plain Text

For simple text, you can include it directly (URL-encoded):

data:text/plain,Hello%20World

Open this in a browser and you'll see "Hello World".

HTML Content

You can embed entire HTML documents:

data:text/html,<h1>Hello</h1><p>This%20is%20a%20data%20URI</p>

Base64-Encoded Images

Binary data like images must be base64-encoded:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...

This can be used directly in an <img> tag:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." alt="Embedded image">

Common MIME Types

The media type tells the browser how to interpret the data:

Type MIME Type Common Use
PNG Image image/png Icons, screenshots
JPEG Image image/jpeg Photos
GIF Image image/gif Animations, simple graphics
SVG Image image/svg+xml Vector graphics, icons
WebP Image image/webp Modern image format
Plain Text text/plain Simple text content
HTML text/html Embedded documents
CSS text/css Embedded stylesheets
JavaScript text/javascript Embedded scripts
JSON application/json Embedded data
PDF application/pdf Embedded documents
Font (WOFF2) font/woff2 Embedded fonts

If no media type is specified, text/plain;charset=US-ASCII is assumed.

Base64 Encoding

Base64 converts binary data into ASCII text using 64 characters (A-Z, a-z, 0-9, +, /). This allows binary content to be safely embedded in text-based formats like URLs and HTML.

The Trade-off

Base64 encoding increases data size by approximately 33%. A 30KB image becomes about 40KB when base64-encoded.

Original Size Base64 Size Increase
1 KB ~1.37 KB +37%
10 KB ~13.3 KB +33%
100 KB ~133 KB +33%

Creating Base64 Data URIs

Several methods to create data URIs:

Command Line (macOS/Linux)

base64 -i image.png | tr -d '\n' | sed 's/^/data:image\/png;base64,/'

JavaScript (Browser)

// From a file input
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const reader = new FileReader();

reader.onload = () => {
  const dataUri = reader.result; // Already in data URI format
  console.log(dataUri);
};

reader.readAsDataURL(file);

Node.js

const fs = require('fs');
const data = fs.readFileSync('image.png');
const base64 = data.toString('base64');
const dataUri = `data:image/png;base64,${base64}`;

When to Use Data URIs

Good Use Cases

  • Small icons and decorative images (under 1-2 KB)
  • Critical above-the-fold images that must display immediately
  • Self-contained HTML documents (single-file reports, emails)
  • Avoiding HTTP requests for very small resources
  • HTML email templates (some email clients block external images)
  • Offline-capable pages without service workers

Bad Use Cases

  • Large images (33% size increase plus no caching)
  • Images used across multiple pages (can't cache separately)
  • Frequently changing images (invalidates entire HTML cache)
  • When HTTP/2 is available (parallel requests are efficient)

Using Data URIs in CSS

Data URIs work in CSS background images:

.icon {
  background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...');
  background-size: contain;
  width: 24px;
  height: 24px;
}

Inline SVG Alternative

For SVG, you can use URL-encoded SVG directly without base64:

.icon {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2L2 7l10 5 10-5-10-5z'/%3E%3C/svg%3E");
}

This is more readable and slightly smaller than base64 for SVG content.

Browser Size Limits

Browsers impose limits on data URI length:

Browser Approximate Limit
Chrome, Firefox, Safari Effectively unlimited (tested to 100+ MB)
Edge (Chromium) Effectively unlimited
Internet Explorer 8 32 KB
Old mobile browsers Varies, often 1-2 MB

While modern browsers handle large data URIs, practical limits exist:

  • Large data URIs slow down HTML parsing
  • Source code becomes unwieldy and hard to maintain
  • Version control diffs become meaningless

Security Considerations

Data URIs can be exploited for malicious purposes:

XSS via Data URIs

A data URI can contain executable HTML/JavaScript:

data:text/html,<script>alert('XSS')</script>

Never:

  • Allow users to provide arbitrary data URIs
  • Generate data URIs from untrusted user input
  • Use data URIs in <iframe> src without sanitization

Data Exfiltration

Data URIs can encode sensitive information that bypasses content security policies in some scenarios.

Content Security Policy

To block data URIs, use Content Security Policy headers:

// Block all data URIs
Content-Security-Policy: default-src 'self'

// Allow data URIs only for images
Content-Security-Policy: img-src 'self' data:; default-src 'self'

Self-Contained HTML Example

Here's a complete, self-contained HTML document using data URIs:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Self-Contained Page</title>
  <style>
    body {
      font-family: system-ui, sans-serif;
      max-width: 600px;
      margin: 2rem auto;
      padding: 1rem;
    }
    .logo {
      width: 100px;
      height: 100px;
    }
  </style>
</head>
<body>
  <!-- SVG logo embedded as data URI -->
  <img class="logo"
       src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='40' fill='%2314b8a6'/%3E%3C/svg%3E"
       alt="Logo">

  <h1>Self-Contained Document</h1>
  <p>This HTML file contains all its resources inline.
     It works without any network connection or external files.</p>

  <!-- Favicon also embedded -->
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='40' fill='%2314b8a6'/%3E%3C/svg%3E">
</body>
</html>

Save this as an HTML file and open it—it works completely offline with no external dependencies.