Base64 Reference

Every Base64 term and concept covered by the Base64 Text & File Encoder/Decoder, collected on one page. Pick a term from the list to see its definition and how it applies to encoding and decoding.

ZERO UPLOAD · ALL LOCAL

What Is Base64 Encoding?

Because binary data , images, audio, certificates, and arbitrary byte sequences , cannot safely travel through text-only channels like email or JSON, engineers needed a standard way to represent any byte sequence using only printable characters. Base64 is that standard.

What is Base64 encoding?

Base64 encoding converts binary data into a string of 64 printable ASCII characters: uppercase A–Z, lowercase a–z, digits 0–9, plus (+), and slash (/), with equals (=) used for padding.1 Every 3 bytes of input become 4 output characters, expanding the data by exactly 33 percent. The decoder reverses the process exactly, recovering the original bytes with no loss.

How Base64 works

Base64 reads input 3 bytes (24 bits) at a time and splits them into four 6-bit groups. Each 6-bit value indexes into a 64-character lookup table where 0 maps to A, 25 maps to Z, 26 maps to a, and 63 maps to /.1 This fixed-size mapping is what makes Base64 deterministic and portable across every programming language and operating system, because the lookup table is defined by the standard rather than left to individual implementation choices.

Seeing the 3-to-4 ratio

Consequently, any byte sequence, regardless of content, produces a predictable ASCII string. Furthermore, the process is reversible: each output character encodes exactly 6 bits, so four characters always recover 3 original bytes. When input length is not divisible by 3, the final group pads with one or two = characters to maintain the 4-character block width. This predictable expansion is what makes Base64 useful for size planning: you can calculate the exact encoded length from the byte count alone, which helps you verify that a payload fits within API gateway limits before you attempt the upload.

This matters because the predictability is the whole point: unlike compression ratios that vary with content, Base64 expansion is fixed, so you can promise a caller that a 6 MB upload becomes exactly 8 MB on the wire without measuring it first. That certainty lets you set gateway and database limits from the original file size alone, which keeps capacity planning simple and removes a whole class of size surprises during a rollout.

Where Base64 is used

Data URIs embed images and fonts directly inside HTML and CSS, eliminating HTTP requests for small assets. SMTP email encodes attachments in Base64 because the protocol was designed for 7-bit ASCII text. JSON APIs use Base64 to transport binary fields , thumbnails, cryptographic signatures, file uploads , inside string properties. PEM certificate files wrap raw ASN.1 bytes in Base64 between BEGIN CERTIFICATE marker and END CERTIFICATE marker markers.2 Kubernetes Secrets store values as Base64-encoded strings in YAML manifests.3 HTTP Basic Auth headers encode username:password pairs in Base64 so the credential string contains only printable ASCII characters that survive header parsing. JWT tokens use Base64url encoding for the header and payload sections, making the tokens safe to pass in URL query strings and HTTP headers without additional escaping.4

Base64 is not encryption

Base64 is encoding, not protection. Anyone with the string can decode it instantly using standard library functions available in every programming language, with no key and no password required. Conversely, encrypted data looks like random bytes until decrypted with the correct key. The two are completely different: encoding changes representation while encryption changes meaning.

Choosing the right protection for sensitive data

Never use Base64 to protect passwords, API keys, or any sensitive data that must remain confidential. For secrets management, use AES-256 encryption, Argon2 hashing for passwords, or an envelope encryption service like AWS KMS or Google Cloud KMS. The confusion between encoding and encryption is common because both transform data into an unreadable format, but the critical difference is that encoding requires no secret key while encryption is designed to be computationally infeasible without one. If you need to test a value before adding it to production code, CapyToolkit keeps those checks local in the browser so sample data is not sent to a server.

From its origins in early text-only mail protocols

Base64 originated in UUENCODE (1980), which encoded binary files for UUCP mail transfer over 7-bit ASCII channels that could not carry raw byte values above 127. UUENCODE used a different alphabet and line-wrapping convention, but the underlying principle of mapping 6 bits to one printable character was the same, and MIME later refined this approach into the Base64 standard that most systems implement today.5 MIME (1992) standardised a cleaner 64-character alphabet: A-Z, a-z, 0-9, +, and /. RFC 4648 (2006) codified both standard Base64 and the URL-safe Base64url variant in a single document that most language standard libraries implement today. Each revision preserved backward compatibility with the previous one, so output produced by a MIME encoder from 1994 still decodes correctly in a modern Base64 library without any special handling.

The 64-character limit is not arbitrary. Each Base64 character carries exactly 6 bits, because 2^6 equals 64. Packing 6-bit groups from 8-bit bytes produces the 3:4 byte-to-character ratio. Using fewer characters would require more output characters per input byte; using more would require characters that break in 7-bit text channels. The alphabet was chosen to survive every ASCII-compatible character set in use at the time of standardisation.

That history matters when you debug interoperability. A string that looks like Base64 may still fail if a protocol expects Base64url, strips padding, or wraps MIME lines at a different width. Keeping the three variants in mind, you can narrow down the failure to a mismatch between what the producer encoded and what the decoder expects, which is usually faster than re-examining the entire encoding pipeline from scratch.

Base64 performance in browsers and runtimes

Base64 encoding and decoding are CPU-bound operations that modern JavaScript engines perform efficiently. For inputs under 10 MB, encoding and decoding in a browser completes in under 10 milliseconds on typical hardware. Beyond 10 MB, time grows linearly with input size, and the allocation of large string objects causes GC pressure that can produce frame drops in UI threads. For large files in browsers, encode on a Web Worker to keep the main thread responsive.

Keeping large browser jobs off the UI thread

On the server side, Node.js Buffer and Python's base64 module delegate encoding to compiled C implementations. For throughput-sensitive applications encoding gigabytes of data per request, use streaming encoders that process chunks rather than loading entire files into memory. The encoding cost rarely dominates unless input exceeds several hundred megabytes per request. For browser-based tools that handle user-selected files, the practical limit is available device memory rather than CPU speed, because the browser must hold the original file, the Base64 string, and any decoded output simultaneously during the conversion process.

Try in the tool

What to look for

  • 64 printable characters, each carrying 6 bits
  • exactly 33% (3 input bytes become 4 output characters)
  • under 10ms to encode/decode inputs under 10 MB on typical hardware

Base64 is encoding, not encryption: anyone can decode it instantly with no key required.

Open the Base64 Text & File Encoder/Decoder tool to try this yourself.

Open the tool →
Sources
  1. 1.

    S. Josefsson, "The Base16, Base32, and Base64 Data Encodings," RFC 4648, IETF, October 2006. https://www.rfc-editor.org/rfc/rfc4648

  2. 2.

    S. Josefsson and S. Leonard, "Textual Encodings of PKIX, PKCS, and CMS Structures," RFC 7468, IETF, April 2015. https://www.rfc-editor.org/rfc/rfc7468

  3. 3.

    Kubernetes, "Secrets," kubernetes.io, accessed June 2026. https://kubernetes.io/docs/concepts/configuration/secret/

  4. 4.

    M. Jones, J. Bradley, and N. Sakimura, "JSON Web Token (JWT)," RFC 7519, IETF, May 2015. https://datatracker.ietf.org/doc/html/rfc7519

  5. 5.

    "Base64," Wikipedia, accessed June 2026. https://en.wikipedia.org/wiki/Base64

FAQ

What Is URL-Safe Base64?

URL-safe Base64 is the URL variant of a text encoding, not a separate cipher or compression format.

What is URL-safe Base64?

URL-safe Base64 (also called Base64url) substitutes + with - and / with _ in the encoding alphabet,1 producing output that requires no percent-encoding when embedded in a URL, HTTP header, or filename. Padding characters (=) are commonly stripped because = has special meaning in URL query strings.2 That distinction matters when you compare protocol examples: the bytes are unchanged, but the receiver may reject the standard alphabet or padding format. Treat the alphabet, padding, and field name as one contract.

How URL-safe Base64 differs from standard Base64

The only alphabet change is two characters: + becomes - and / becomes _. All other 62 characters (A-Z, a-z, 0-9) are identical between the two variants. Consequently, the encoded length and 33% expansion rate stay the same.1 Padding is usually stripped in URL-safe contexts, because the decoder infers the correct byte count from the string length. Switching between variants is trivial: replace + with -, / with _, and strip or restore = as needed. Because these are text substitutions, no re-encoding of the underlying bytes is required.

Matching the alphabet to the transport

Treat the alphabet choice as part of the transport contract between your service and its clients. A JWT header, OAuth PKCE parameter, and filename token can all carry the same bytes, but each receiver expects the URL-safe alphabet and often expects no padding. When your API documentation says a field is Base64-encoded without specifying the variant, clients will guess wrong roughly half the time, which is why the most reliable APIs name the exact encoding in the field description or use a structured format like a JSON object with separate value and encoding fields.

This matters because a mismatch surfaces far from the source: the client encodes with one alphabet, the server decodes with the other, and the failure shows up as an unreadable token in a log rather than a clear encoding error at the boundary. By the time the bad token reaches the server, the original encode step is long gone from the debugger's view, so naming the exact variant in the contract saves a painful round trip through two codebases.

Where URL-safe Base64 is used

JSON Web Tokens (JWTs) use Base64url for the header and payload sections.3 OAuth PKCE flows encode the code challenge and verifier as Base64url.4 Google Cloud Storage signed URL parameters are Base64url. Python's urlsafe_b64encode(), Java's Base64.getUrlEncoder(), and Go's base64.URLEncoding all implement the same RFC 4648 §5 alphabet. Furthermore, some file systems prohibit / in filenames , Base64url avoids that restriction without any additional escaping. WebAuthn uses Base64url for challenge values and credential IDs that travel between the browser and the relying party server.5 Signed cookies in frameworks like Rails and Django often use Base64url to ensure the cookie value survives transport through HTTP headers and URL query parameters without corruption from special characters.

Common pitfalls when switching between variants

Mixing standard and URL-safe Base64 is the most frequent encoding bug. A JWT library that produces Base64url will fail to decode a string encoded with standard Base64.getEncoder() because + and / characters appear garbled. Conversely, passing a Base64url string to a MIME decoder that expects standard Base64 corrupts output silently in some languages. Always check which variant a library uses before passing encoded data between systems. Padding mismatches cause a second class of bug: some decoders reject unpadded input; others reject padded input , match the variant and padding behavior at both ends.

Preventing silent alphabet mismatches

Document the expected alphabet beside fields that cross service boundaries. In API specs, name the field tokenBase64Url instead of tokenBase64 when the value uses - and _, and reject strings containing the opposite alphabet during validation. Adding a validation regex that only allows the expected alphabet characters catches encoding mismatches at the API boundary before they propagate into downstream services, where the resulting corruption is much harder to trace back to the original encoding error.

Padding rules across URL-safe Base64 protocols

Because padding characters (=) carry special meaning in URL query strings as key-value separators, most protocols that use URL-safe Base64 also strip padding. JWT tokens strip = from all three sections.3 OAuth PKCE code challenges use unpadded Base64url. WebAuthn credential IDs in the browser Web Authentication API are Base64url without padding.5

Not all URL-safe contexts strip padding. Google Cloud Storage signed URL parameters use Base64url with padding intact. Azure shared access signatures include = characters in Base64url fields. Before stripping or adding padding, check the target protocol's specification rather than assuming the URL-safe variant always means no padding. A decoder that expects padding will reject an unpadded string; a parser that treats = as a delimiter will break on a padded one.

URL-safe Base64 in browser Web Crypto operations

The Web Crypto API produces binary output as ArrayBuffer and Uint8Array values. Converting cryptographic key material or a digital signature to a URL-safe Base64 string requires manual encoding: convert the Uint8Array to a binary string using String.fromCharCode(), pass that string to btoa(), then apply the character substitution. The PKCE code verifier uses this exact pattern:4 generate 32 random bytes with crypto.getRandomValues(), encode to Base64url, and the result is the 43-character verifier string the authorization server expects.

Encoding Web Crypto bytes in browsers

For decoding WebAuthn credential IDs from a server response, reverse the substitution before passing to atob(). Libraries such as jose and @simplewebauthn/browser handle this internally, but understanding the conversion explains why manual atob() calls fail on Base64url strings without preprocessing. If you implement the conversion yourself, writing a small utility function that wraps the character substitution, padding, and btoa() call keeps the logic testable and reusable across your codebase, rather than scattering raw string replacements throughout your authentication handlers.

Try in the tool

What to look for

  • A-Z (0-25), a-z (26-51), 0-9 (52-61), - (62), _ (63)
  • 32 random bytes encode to a 43-character Base64url string
  • usually stripped (JWT, PKCE, WebAuthn), but not universal (GCS signed URLs keep it)

Only two alphabet characters differ from standard Base64 (+/- and //_), so converting between the two variants is a text substitution, not a re-encode.

Open the Base64 Text & File Encoder/Decoder tool to try this yourself.

Open the tool →
Sources
  1. 1.

    S. Josefsson, "The Base16, Base32, and Base64 Data Encodings," RFC 4648, IETF, October 2006. https://www.rfc-editor.org/rfc/rfc4648

  2. 2.

    Mozilla Developer Network, "Percent-encoding," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding

  3. 3.

    M. Jones, J. Bradley, and N. Sakimura, "JSON Web Token (JWT)," RFC 7519, IETF, May 2015. https://datatracker.ietf.org/doc/html/rfc7519

  4. 4.

    N. Sakimura, J. Bradley, and N. Agarwal, "Proof Key for Code Exchange by OAuth Public Clients," RFC 7636, IETF, September 2015. https://www.rfc-editor.org/rfc/rfc7636

  5. 5.

    W3C, "Web Authentication: An API for accessing Public Key Credentials - Level 2," w3.org, April 2021. https://www.w3.org/TR/webauthn-2/

FAQ

What Is Padding in Base64?

A Base64 string ends with equals signs when the original byte count did not divide cleanly into three-byte groups.

What is Base64 padding?

Base64 padding appends one or two = characters to the end of an encoded string when the input length is not divisible by 3.1 One = marks a final group with 2 input bytes (3 Base64 characters + 1 padding). Two == marks a final group with 1 input byte (2 Base64 characters + 2 padding). Input divisible by 3 produces no padding.

How padding works

Base64 processes input 3 bytes at a time. When the last group has only 1 byte, it encodes 2 characters and adds == to fill the 4-character block. When the last group has 2 bytes, it encodes 3 characters and adds = to fill the block. Consequently, the output length is always a multiple of 4.1 Decoders use = characters to determine how many bytes the last block contains , == means 1 byte, = means 2 bytes, no padding means 3 bytes. This makes the encoding self-describing at block boundaries.

Reading the final block

Padding tells a decoder how many bytes to keep from the final block. That matters when the receiver cannot infer the original byte count from a surrounding schema or protocol field. Without padding, a decoder reading the last 4-character block has no way to know whether the original data ended with 1, 2, or 3 bytes, because all three cases produce valid-looking output that differs only in how many trailing bytes should be discarded during the final decode step.

This matters because a decoder that guesses wrong discards the wrong number of bytes and returns a string that is subtly corrupted rather than obviously broken, which is the worst kind of bug because the failure is silent. A downstream parser may accept the malformed result and store it, so the corruption propagates into other systems before anyone decodes the original value again and sees that the tail bytes were wrong all along.

When to strip padding

JWT tokens require Base64url without padding , = characters are stripped from header and payload segments.2 URLs containing Base64 in query parameters may interpret = as part of the key=value syntax, causing parse errors. Conversely, MIME email and PEM files require padding.3 Because many decoders handle both padded and unpadded input, stripping = is often safe , but always verify what the target system expects. Restoring stripped padding requires adding = characters until the length is a multiple of 4.

Matching receiver expectations

Do not strip padding because a string looks cleaner. Strip it only when the receiving protocol says to do so, and restore it before passing the value to a strict decoder. The safest approach is to always include padding in your own output and let the consumer strip it if needed, because adding padding back to a string that was originally unpadded requires knowing the original byte count, which is information the receiver may not have.

Common padding errors

Incorrect padding is among the most frequent Base64 decoding errors. An 'Invalid padding' error means the input length modulo 4 does not match the number of = characters. This usually happens when a string was truncated, a line-wrapped block was re-joined without removing CRLF, or a standard Base64 string was passed to a URL-safe decoder. Fixing it: strip all whitespace, count characters modulo 4, and append 0, 1, or 2 = characters to reach a multiple of 4. Building on this, re-encoding the corrected string often reveals whether the original data was truncated or just improperly padded.

Diagnosing invalid padding

If adding padding does not make the value decode, inspect the source for truncation, copied line breaks, or the wrong alphabet. Padding repairs block alignment, but it cannot restore bytes that were never included. A useful debugging technique is to compare the byte length of the decoded output against the expected file size or magic byte pattern, because a string that decodes without errors but produces the wrong number of bytes almost always indicates that the source was truncated or that line-wrapping characters were accidentally included in the encoded payload.

Padding handling differs across popular languages

Programming language standard libraries treat padding inconsistently at the decoder level.4 Python's base64.b64decode() adds missing padding automatically; Go's base64.StdEncoding.DecodeString() returns an error for unpadded input, while base64.RawStdEncoding.DecodeString() accepts unpadded strings directly. Java's Base64.getDecoder() accepts both padded and unpadded input. JavaScript's atob() requires padded input and throws DOMException on unpadded strings.

These differences cause bugs when a Python library sends unpadded Base64 to a JavaScript client: the atob() call fails with an 'invalid character' error that resembles a data corruption issue rather than a padding problem.5 Always document whether your API sends padded or unpadded Base64, and match the decoder configuration to the sender's convention. Testing with a string whose length modulo 4 is 2 (producing two = padding characters) reveals decoder strictness quickly.

Because Base64 processes bytes in groups of three

Any input whose length is not a multiple of 3 generates an incomplete final group. Padding fills that gap to maintain uniform 4-character block width. A decoder reading 4 characters at a time treats = as a sentinel: one = means discard the last decoded byte, two == means discard the last two bytes.

Without padding, a decoder can still recover the correct bytes from the string length alone. A string of length 4n+2 holds 1 byte in the final group; 4n+3 holds 2 bytes. This is why many protocols drop padding without loss of information. The trade-off is that decoders must check the string length explicitly instead of relying on the = sentinel, adding a small parsing step that naive decoders sometimes skip, causing off-by-one errors on the final byte.

Try in the tool

What this page covers

  • Python base64.b64decode() adds missing padding automatically
  • Go base64.StdEncoding errors on unpadded input; base64.RawStdEncoding accepts it
  • Java Base64.getDecoder() accepts both padded and unpadded input
  • JavaScript atob() requires padded input and throws DOMException otherwise

Open the Base64 Text & File Encoder/Decoder tool to try this yourself.

Open the tool →
Sources
  1. 1.

    S. Josefsson, "The Base16, Base32, and Base64 Data Encodings," RFC 4648, IETF, October 2006. https://www.rfc-editor.org/rfc/rfc4648

  2. 2.

    M. Jones, J. Bradley, and N. Sakimura, "JSON Web Token (JWT)," RFC 7519, IETF, May 2015. https://datatracker.ietf.org/doc/html/rfc7519

  3. 3.

    S. Josefsson and S. Leonard, "Textual Encodings of PKIX, PKCS, and CMS Structures," RFC 7468, IETF, April 2015. https://www.rfc-editor.org/rfc/rfc7468

  4. 4.

    "Base64," Wikipedia, accessed June 2026. https://en.wikipedia.org/wiki/Base64

  5. 5.

    "atob()," MDN, developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/API/atob

FAQ

Base64 vs Hex: Which Encoding to Use?

Base64 and hex both turn bytes into text, but they optimize for different jobs.

What is Base64 vs hex encoding?

Base64 encoding represents 3 bytes as 4 printable ASCII characters, producing a 33% size overhead1. Hex encoding represents each byte as exactly 2 hexadecimal digits (0–9, a–f), doubling the size. Base64 is more compact and better for binary transport; hex is fully human-readable one byte at a time and suits cryptographic values, hashes, and debugging output.

Size comparison

The size difference compounds quickly. A 32-byte SHA-256 hash encodes to 44 Base64 characters (32 × 4/3, rounded to a multiple of 4) but 64 hex characters (32 × 2). Consequently, Base64 saves 20 characters , 31% less than hex , for every SHA-256 hash in a response payload. For a 100 KB image, Base64 produces roughly 133 KB of text while hex produces 200 KB. Furthermore, line-wrapped MIME Base64 adds CRLF pairs every 76 characters2, which partially offsets the size advantage in human-readable scenarios.

Comparing payload size

Choose Base64 when payload size matters and the receiver expects text-safe binary transport. Choose hex when the representation will be copied, audited, or compared by a human who needs byte boundaries visible. The 31% savings from Base64 over hex adds up fast in high-volume APIs: a service that returns 10,000 SHA-256 hashes per response saves roughly 200 KB of payload size by choosing Base64, which directly reduces bandwidth costs and improves response times for clients on slow connections.

This matters because the choice is usually locked in by a standard, not a preference: once an API returns Base64 hashes, a client that logs them as hex spends hours chasing a phantom mismatch that is just two encodings of the same bytes. The two strings look completely different as text even though the underlying data is identical, so without agreeing on one representation up front, every comparison between services becomes a manual, error-prone decoding exercise.

Readability and debugging

Hex wins on readability for byte-level inspection. Each hex digit pair maps to exactly one byte, making it trivial to identify byte boundaries, spot null bytes (00), or compare two binary values side by side. Base64 groups 3 bytes per 4 characters, so a single-byte change shifts all subsequent characters , a debugger sees no clean alignment. Conversely, hex output is verbose for large payloads: a 256-byte AES key is 512 hex characters. For anything read or compared by humans , checksums, UUIDs, MAC addresses, debug logs , hex is the standard choice.

Reading byte boundaries

Base64 is compact, but its character blocks do not line up with byte boundaries. That makes it efficient for transport and awkward for manual inspection when you need to find a single changed byte. When you compare two Base64 strings side by side, a single-byte difference at the start of the input changes every subsequent character in the output, because the 6-bit grouping shifts the entire alignment, making visual diffing practically impossible without first decoding both strings back to their raw byte representation.

When to choose each

Choose Base64 for data transport: email attachments (MIME), data URIs, binary JSON fields, TLS certificates (PEM), and JWT payloads. The compact output and standard library support across all platforms make it the default for binary-over-text scenarios where payload size matters. Choose hex for cryptographic outputs: hash digests (SHA-256, MD5), HMAC signatures, encryption keys, and git commit IDs. Hex is the convention in security tooling, Linux command-line utilities, and most cryptographic APIs. Mixing them causes silent failures: a SHA-256 comparison fails if one side is Base64-encoded and the other is hex, because the two representations of the same bytes look completely different as text strings.

Choosing by receiver expectations

Start with the protocol, not personal preference. If a standard names the representation, use that representation; if no standard exists, pick Base64 for compact transport or hex for human-readable diagnostics. Documenting the chosen encoding in your API schema or README prevents the all-too-common situation where a new team member assumes the opposite encoding and spends hours debugging a mismatch that produces no obvious error message.

Protocol standards specify Base64 or hex explicitly

Major protocols define which encoding to use, and mixing them causes compatibility failures that are hard to debug. TLS certificate fingerprints appear as hex in browser developer tools and OpenSSL output but as Base64 in some certificate pinning libraries. HTTP Content-MD5 headers use Base64. Digest authentication uses MD5 hashes3. The two representations are not interchangeable: comparing a Base64-encoded fingerprint against a hex fingerprint always produces a mismatch even when the underlying bytes are identical.

JWT uses Base64url for the header and payload. OAuth authorization codes are typically hex or random Base64url depending on the provider. SSH public keys use Base64 for the key blob but hex for fingerprints. Confirm what the target protocol's specification documents before implementing encoding, rather than inferring behavior from one example.

When converting between Base64 and hex

Converting between Base64 and hex requires going through the raw bytes as an intermediate representation. There is no direct shortcut: Base64-decode the string to bytes, then hex-encode those bytes, or reverse the process. In Python: bytes.fromhex(hex_str) gives the raw bytes, then base64.b64encode(raw_bytes) gives Base64. In JavaScript: Buffer.from(hexStr, 'hex').toString('base64') converts in Node.js in one step4.

For local verification, use Python's interactive shell: import base64, binascii; binascii.hexlify(base64.b64decode(b64_str)).decode() confirms both representations carry identical bytes5. Never send private keys, API secrets, or passwords to an external conversion service. Verify sensitive values locally, then discard the decoded output after the check. In Go, the encoding/hex and encoding/base64 packages work together through the same io.Reader and io.Writer interfaces, so you can chain a hex decoder into a base64 encoder in a single pipeline for streaming conversion of large payloads without loading the entire content into memory.

Try in the tool

What to look for

  • 44 Base64 characters vs 64 hex characters
  • ~133 KB as Base64 vs ~200 KB as hex
  • 40 hex characters (SHA-1, 20 bytes) - always hex, never Base64

Comparing a Base64-encoded value against a hex-encoded value of the same bytes always mismatches as text, even though the underlying data is identical.

Open the Base64 Text & File Encoder/Decoder tool to try this yourself.

Open the tool →
Sources
  1. 1.

    S. Josefsson, "The Base16, Base32, and Base64 Data Encodings," RFC 4648, IETF, October 2006. https://www.rfc-editor.org/rfc/rfc4648

  2. 2.

    N. Freed and N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies," RFC 2045, IETF, November 1996. https://www.rfc-editor.org/rfc/rfc2045

  3. 3.

    J. Franks et al., "HTTP Authentication: Basic and Digest Access Authentication," RFC 2617, IETF, June 1999. https://datatracker.ietf.org/doc/html/rfc2617

  4. 4.

    Node.js, "Buffer," nodejs.org, accessed June 2026. https://nodejs.org/docs/latest/api/buffer.html

  5. 5.

    Python, "base64 — Base16, Base32, Base64, Base85 Data Encodings," python.org, accessed June 2026. https://docs.python.org/3/library/base64.html

FAQ

What Is a Data URI?

Because HTTP requests for external resources add network latency and dependency on additional DNS lookups, browsers support data URIs that embed resource content directly in the document, eliminating the need for a separate HTTP request entirely.1

What is a data URI?

A data URI is a URL that begins with the data: scheme and embeds file content inline, using the format data:[<mediatype>][;base64],<data>.1 The media type specifies the MIME type (such as image/png or text/css). The ;base64 flag indicates Base64 encoding; without it the data is URL-encoded plain text. Browsers treat data URIs identically to external URLs in most contexts.2

Data URI format and examples

A PNG image data URI looks like: data:image/png;base64,iVBORw0KGgo... (followed by the full Base64 string). An SVG inline as data URI uses: data:image/svg+xml;base64,PHN2Zy... or, for small SVGs, the more compact URL-encoded form: data:image/svg+xml,%3Csvg.... CSS background images accept data URIs in the url() function. The media type defaults to text/plain;charset=US-ASCII when omitted. Consequently, data URIs can represent any resource type a browser can display, not just images, including fonts, PDF documents, and CSS stylesheets that the browser would otherwise need to fetch as separate resources.

Building the data URI string

A complete data URI needs three parts: the correct media type, the optional ;base64 flag for binary data, and the encoded payload. Missing any part changes how the browser interprets the resource. Omitting the media type entirely causes the browser to default to text/plain;charset=US-ASCII, which means a perfectly valid PNG will render as garbled text characters instead of an image, and the only symptom is a broken image icon with no console error to guide you toward the missing media type declaration.

This matters because the failure mode is invisible: the browser does not throw, the network tab shows a 200, and the asset simply never renders, so the missing media type is easy to miss unless you already suspect it. A teammate debugging the page sees a blank slot and a successful request, concludes the problem is elsewhere, and only finds the real cause after inspecting the raw bytes the browser actually received.

Size limits and performance trade-offs

Internet Explorer historically limited data URIs to 32 KB.3 Modern browsers handle data URIs up to 2 MB in Chromium-based browsers and Safari.2 Base64 encoding adds 33% overhead, so a 100 KB PNG becomes roughly 133 KB inline in your HTML. Furthermore, data URIs are not cached independently by the browser , the inline data re-downloads with every page that includes it. External resources cache at the CDN and in the browser independently of the document, so for assets used on more than one page, a data URI increases total data transfer rather than reducing it.

Weighing cache behavior

The request count drops when you inline an asset, but the cache boundary moves. The image becomes part of the HTML or CSS response, so changing the icon changes the whole document cache key. This means that a single-pixel change to a small inlined icon invalidates the entire stylesheet or page for every user, whereas the same change to an external resource only invalidates the cached icon file while the rest of the document remains cached and fast.

When to use data URIs vs external resources

Data URIs make sense for small, single-use assets: icons under 4 KB, loading spinners, tiny SVG logos used on exactly one page. Email templates benefit from data URIs because many email clients block external images by default. Conversely, production web pages should use CDN-hosted external resources for anything used on more than one page, any asset larger than a few kilobytes, and anything that needs independent cache control or versioning. Using a data URI for a 500 KB background image inlines 667 KB of Base64 into your HTML, blocks the parser while it decodes, and re-downloads on every page visit.

Selecting assets for inlining

Use data URIs when the asset is tiny, local to one document, and unlikely to change often. If the file is reused across pages, versioned independently, or already served by a CDN, an external URL usually gives better cache behavior. A practical rule of thumb is to inline only assets under 4 KB that appear on a single page, because the 33% encoding overhead on a 4 KB file adds just 1.3 KB to your document, while the eliminated HTTP request saves 50 to 200 milliseconds of latency on first load.

Automating data URI generation in build pipelines

Generating data URIs by hand is error-prone for anything beyond the smallest icons. Modern build tools automate the conversion at compile time. Vite uses the assetsInlineLimit option (defaulting to 4 KB) to inline assets as Base64 data URIs inside the JavaScript bundle.4 Webpack achieves the same result with url-loader. Both tools apply the 1.333 multiplier to every inlined file; your bundle analysis report reflects the actual encoded size.

To generate a data URI manually, read the file as binary, Base64-encode the bytes, and prepend the correct data:[mediatype];base64, prefix. Confirm the media type matches the actual file format before use. An incorrect type (image/jpg instead of image/jpeg, for example) causes silent rendering failures in some browsers that will not produce a helpful console error.

Because SVG files are text-based XML

SVG files behave differently from binary formats under data URI encoding because two encoding options exist with different size characteristics. A Base64-encoded SVG data URI uses data:image/svg+xml;base64,PHN2Zy.... A URL-encoded SVG uses data:image/svg+xml,%3Csvg.... URL encoding avoids the 33% Base64 overhead for SVG by percent-encoding only the characters that require escaping, which for most SVG content produces a shorter final string because the SVG character set is limited to a small subset of ASCII.

For very small SVGs (under 1 KB), URL encoding saves 15 to 25% of the final string length compared to Base64.5 For SVGs with many special characters or complex paths, Base64 may produce a shorter result because URL encoding expands each non-ASCII byte to three characters (%XX). Test both representations and choose the shorter output. In CSS background-image declarations, quote the URL-encoded form inside double quotes to guarantee browser compatibility across all major rendering engines.

Try in the tool

What to look for

  • data:[<mediatype>][;base64],<data>
  • 32 KB
  • up to 2 MB
  • 15-25% shorter than Base64 for SVGs under 1 KB

Data URIs are not cached independently; the inline data re-downloads with every page that includes it, unlike an external resource.

Open the Base64 Text & File Encoder/Decoder tool to try this yourself.

Open the tool →
Sources
  1. 1.

    T. Berners-Lee, L. Masinter, and M. McCahill, "Uniform Resource Identifiers (URI): Generic Syntax," RFC 2397, IETF, August 1998. https://www.rfc-editor.org/rfc/rfc2397

  2. 2.

    Mozilla Developer Network, "data: URLs," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data

  3. 3.

    Microsoft, "data Protocol," learn.microsoft.com, accessed June 2026. https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/jj710206(v=vs.85)

  4. 4.

    "build.assetsInlineLimit," vitejs.dev, accessed June 2026. https://vitejs.dev/config/build-options.html#build-assetsinlinelimit

  5. 5.

    C. Coyier, "Probably Don't Base64 SVG," css-tricks.com, 2016. https://css-tricks.com/probably-dont-base64-svg/

FAQ