- Published on
Base64 PNG Images: Encode, Embed, and Decode
A practical PNG-focused guide to Base64 encoding, data URLs, size trade-offs, and cross-platform examples.
- Authors

- Name
- Adam Johnston
- @admjski

Base64 PNG Images: Encode, Embed, and Decode
A PNG file is binary data. Base64 represents those bytes with text characters so the data can be carried in text-oriented formats (RFC 4648). A data URL combines a media type, an encoding marker, and that Base64 text into a URL such as data:image/png;base64,... (RFC 2397).
Base64 is an encoding, not compression, encryption, or access control. It does not make an image private or safer to share. PNG already uses lossless compression, and converting its bytes to Base64 makes the encoded representation larger.
What the size change means
Base64 maps each group of three input bytes to four output characters, adding padding when needed. The encoded length is 4 × ceil(input bytes / 3), before a data URL prefix or line breaks. For larger inputs this approaches one-third more characters than the original bytes; padding makes the percentage larger for very small payloads. The image's visual quality and pixel data do not change.
A small icon used once in a single-file demo may be convenient to embed. For larger PNGs, frequently reused images, or assets that should be cached separately, a normal .png file is usually easier to maintain and can be shared across pages. There is no universal byte threshold at which embedding becomes slower: compare the actual image size, request pattern, caching, compression and delivery setup.
Encode a PNG
Linux and macOS
The -w option is not portable across the GNU and BSD versions of base64. This pipeline removes line breaks from the output on common Linux and macOS systems:
base64 < logo.png | tr -d '\n' > logo.b64
The output contains only the encoded text. It does not include the data URL prefix.
Windows
certutil writes header and footer lines around its encoded output. For a simple cross-platform alternative, Python or Node.js can produce the Base64 text without those lines. If using certutil, remove the BEGIN CERTIFICATE and END CERTIFICATE lines before using its content as the data portion of a data URL.
Node.js
import { readFile, writeFile } from 'node:fs/promises'
const png = await readFile('logo.png')
const encoded = png.toString('base64')
await writeFile('logo.b64', encoded)
console.log(`Read ${png.length} bytes; wrote ${encoded.length} Base64 characters.`)
The printed lengths come from the actual file at runtime; no image-specific benchmark is implied.
Python
from base64 import b64encode
from pathlib import Path
png_bytes = Path('logo.png').read_bytes()
encoded = b64encode(png_bytes).decode('ascii')
Path('logo.b64').write_text(encoded, encoding='ascii')
The binary mode matters: it preserves the exact PNG bytes on platforms that distinguish text and binary file I/O.
Make a PNG data URL
Prefix the encoded bytes with the PNG media type and ;base64,:
const dataUrl = `data:image/png;base64,${encoded}`
Then use the URL where an image URL is accepted:
<img src="data:image/png;base64,iVBORw0KGgo..." alt="Small blue logo" />
The shortened example is illustrative, not a complete image. In a real document, use the complete output string. In CSS, a data URL can also be used in url(...), with the same size and policy trade-offs.
Decode the data back to PNG bytes
A data URL has a prefix before the comma. Remove that prefix before decoding the Base64 payload. In Node.js:
import { writeFile } from 'node:fs/promises'
const dataUrl = 'data:image/png;base64,...'
const prefix = 'data:image/png;base64,'
if (!dataUrl.startsWith(prefix)) {
throw new Error('Expected a Base64 data URL')
}
const payload = dataUrl.slice(prefix.length)
const validBase64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
if (!validBase64.test(payload)) throw new Error('Invalid Base64 payload')
const pngBytes = Buffer.from(payload, 'base64')
await writeFile('decoded.png', pngBytes)
The equivalent Python decode is:
from base64 import b64decode
from pathlib import Path
payload = 'iVBORw0KGgo...'
png_bytes = b64decode(payload, validate=True)
Path('decoded.png').write_bytes(png_bytes)
The shortened payload is only illustrative; use the complete Base64 string. For either runtime, validate the media type and input source as appropriate for your application; successful decoding alone does not prove the bytes are a valid or safe PNG.
Browser and security considerations
Browsers accept data URLs in supported contexts, subject to browser behavior, document policy and size constraints. A Content Security Policy img-src directive can allow or block them: for example, img-src 'self' data: permits same-origin and data-URL images for that directive. Add data: only when the application needs it; allowing a source is not sanitization and does not make attacker-controlled input safe. Prefer a narrow policy and validate content at the point where untrusted data enters the application.
Do not treat Base64 as secret storage. Anyone who can read the string can decode it. Avoid putting sensitive images or confidential content in public HTML, CSS, logs or shareable URLs. For email, check the specific clients and message-size limits you support; support for inline images and data URLs varies, so do not assume every recipient will display them.
When to use a separate PNG file
Prefer a normal image URL when the asset is substantial, appears on multiple pages, needs independent caching or updating, or should be discoverable and managed as an image resource. Consider embedding a tiny, non-sensitive image when making a self-contained document is more valuable than separate caching. Measure your own page rather than assuming Base64 improves speed.
Base64 is useful when its text-friendly representation solves a specific delivery problem. For PNGs, it does not replace image optimization, file hosting, or security controls.
Further looks

