Uttir
By Uttir 7 min read

Base64 Encoding Explained: What It Is and How It Works

A practical guide to Base64 encoding: what it is, how it works, when to use it, and when to use something else. With examples and a free browser-based encoder.

Base64 is a way to represent any binary data (files, images, encrypted bytes) using only printable text characters. It's not encryption — anyone can decode it — and it costs about 33% more space than the original. It's useful for embedding images in HTML, sending binary data over text-only channels like email, and storing credentials in config files.

You've seen Base64 a thousand times, even if you didn't know it. It's the gibberish-looking text in email attachments, the long string in a data URL, the credentials block in your .env file, the "eyJ..." prefix in JWT tokens. Once you know what it is, you start seeing it everywhere.

This guide explains what Base64 is, how it works under the hood, when to use it, and — just as importantly — when not to use it. If you just need to encode or decode something right now, skip to the bottom for a free browser-based Base64 encoder.

What Base64 actually is

Base64 is an encoding scheme, not an encryption scheme. It takes any sequence of bytes and represents it as a string of printable ASCII characters, drawn from a 64-character alphabet: the 26 uppercase letters, the 26 lowercase letters, the 10 digits, plus + and /, with = used for padding.

That alphabet is the whole trick. Computers store everything as numbers, but most systems that handle text — email, JSON, XML, URLs, HTTP headers, configuration files — were designed for human-readable ASCII. If you want to send a photo, a PDF, an SSL certificate, or any other binary data through one of those systems, you need a way to write the binary bytes using only characters that won't get mangled. Base64 is one such way.

The 64-character alphabet was chosen because it covers all the printable ASCII characters that are safe to put in pretty much any text context. (The URL-safe variant uses - and _ instead of + and /, so the encoded string can be safely embedded in a URL without percent-encoding.)

How it works (the short version)

Every 3 bytes of input (24 bits) becomes 4 characters of output (also 24 bits, but spread across 4 Base64 characters, each representing 6 bits). When the input length isn't a multiple of 3, the output is padded with = characters to make the math work out.

Here's the input "Hi!" encoded step by step:

Input bytes:    H        i        !
                0x48     0x69     0x21
                01001000 01101001 00100001
                └──────┬──────┬──────┘
                   24 bits total

Split into 6-bit chunks:
                010010 000110 100100 100001
                   │      │      │      │
Decimal:        18     6      36     33

Index into Base64 alphabet:
                S      G      k      h

Output: "SGkh"

That's the entire algorithm. Every 3 bytes becomes 4 Base64 characters. For inputs that aren't a multiple of 3 bytes, the last group has 1 or 2 bytes, and the output gets 1 or 2 = padding characters.

Because every 3 input bytes become 4 output characters, Base64 increases the size of the data by exactly 33% (the 4/3 ratio). A 1 MB file becomes a 1.33 MB Base64 string. This is important: Base64 is not compression. It's always larger than the input.

What Base64 is good for

Base64 is the right tool when you need to put binary data into a text-only context and don't mind the size overhead. Common cases:

Embedding images in HTML and CSS

The data: URL scheme lets you embed images directly in HTML or CSS without a separate HTTP request. The image is encoded as a Base64 string and inlined:

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

This is great for small images (icons, logos, avatars) that would otherwise be a separate network request. For larger images, it's worse than just linking to the file because the Base64 data can't be cached separately and can't be lazy-loaded. The Image to Base64 converter does exactly this.

Sending binary data over email

Email was originally a 7-bit ASCII medium. Attachments — which are binary — have to be encoded as text to survive the trip. Base64 (and the related quoted-printable encoding) is how this works. When you attach a PDF to an email, the mail client encodes it as Base64, sends it, and the receiving client decodes it back to the PDF.

Storing credentials and tokens

HTTP Basic Authentication sends credentials as username:password encoded in Base64. JWT tokens are JSON objects encoded in Base64URL (a URL-safe variant). API keys are often generated as random bytes and shown to you as Base64 so they're copy-pasteable.

Important: Base64-encoded credentials are not encrypted. Anyone who sees the string can decode it instantly. The Base64 decoder will turn "c3VzdW46aGFja2VyMTIz" back into "susan:hacker123" in a fraction of a second. If you need to protect credentials, use a real encryption scheme or a password manager — never Base64 alone.

Storing binary data in JSON and YAML

JSON and YAML are text formats and don't have a clean way to embed raw binary. The convention is to Base64-encode the binary and include the result as a string. If you've ever seen a Kubernetes Secret with a data field, that's what's happening.

Putting binary in URLs

URLs are text and have a limited character set. Some characters need to be percent-encoded, others just don't work. The URL-safe Base64 variant (- and _ instead of + and /, no padding) was designed for this. You'll see it in JWT tokens, signed URLs, and base64url-encoded JSON in OAuth flows.

What Base64 is bad for

Just as important: when not to use Base64.

It's not encryption

Base64 is fully reversible by anyone with a decoder (which is built into every programming language). If you see "Base64-encrypted" anywhere, the author is confused or lying. For actual data protection, use:

  • Passwords: a password manager or a KDF like bcrypt or Argon2.
  • Files at rest: AES-256-GCM (or whatever your threat model calls for).
  • Data in transit: TLS (the "s" in HTTPS).
  • API secrets: a secrets manager, not Base64.

It's not compression

Base64 is always 33% larger than the original. If you need to make data smaller, use actual compression: gzip, brotli, or zstd. For network transfer, modern servers can compress on the fly with Content-Encoding: gzip, and the browser decompresses automatically.

It's not great for large files

A 10 MB image becomes a 13.3 MB Base64 string. That's a 33% bandwidth tax for no real benefit if the destination supports binary transfer. The only reason to use it is when the channel is text-only, like email or a JSON config.

It makes diffs unreadable

Storing binary data as Base64 in version control produces diffs that look like random character changes on every commit, even when the underlying binary changed by one byte. If you have a choice, store binary files as actual binary (Git LFS, or just regular files) and only encode when you have to.

Common pitfalls

A few things that go wrong when you start using Base64 in real projects:

Padding

Standard Base64 uses = to pad the output to a multiple of 4 characters. The URL-safe variant usually omits padding. Different libraries handle this differently, and the result is that SGkh and SGkh are equivalent, but SGkh and SGk= are sometimes treated differently by strict decoders. If your decoder is failing, the first thing to check is whether the padding is correct.

Line breaks

Some Base64 implementations (notably older email encoders) insert a newline every 76 characters. If you're parsing Base64 from an email and the decoder is failing on otherwise-valid input, strip the newlines first.

URL safety

Standard Base64 uses + and /, which are not URL-safe. The URL encoder would percent-encode them, but that adds 6% to the size and looks ugly. Use the URL-safe Base64 variant (called Base64URL, with - and _) for anything that goes in a URL.

Character encoding

Base64 takes a sequence of bytes. The bytes are interpreted as raw binary — there's no concept of "this is UTF-8 text" inside Base64. If you're encoding the string "résumé", you need to decide: encode the UTF-8 bytes of those characters, or encode the codepoints, or encode the codepoints as UTF-16? Different choices produce different Base64 output, and the decoder needs to know which one you used. The safe default is always "encode as UTF-8, then Base64 the bytes."

How to use it in code

Every mainstream language has Base64 built in. A few examples:

# JavaScript (browser)
const encoded = btoa('Hello, world!');  // "SGVsbG8sIHdvcmxkIQ=="
const decoded = atob(encoded);            // "Hello, world!"

# JavaScript (Node.js)
const encoded = Buffer.from('Hello').toString('base64');
const decoded = Buffer.from(encoded, 'base64').toString();

# Python
import base64
encoded = base64.b64encode(b'Hello, world!')  # b'SGVsbG8sIHdvcmxkIQ=='
decoded = base64.b64decode(encoded)            # b'Hello, world!'

# Shell
echo -n 'Hello, world!' | base64   # SGVsbG8sIHdvcmxkIQ==

For quick one-off encoding without writing code, the Uttir Base64 encoder handles plain text, file uploads, and the URL-safe variant. The decoder does the reverse, with strict and lenient modes for dealing with padding issues.

Base64 vs Base32 vs Base16 (hex)

Base64 is one of several "Base-N" encodings. The other common ones:

  • Base16 (hexadecimal). Uses 0-9 and A-F, so each byte is two characters. 100% larger than the input. Easy to read and type, used everywhere humans need to look at bytes (memory dumps, color codes, MAC addresses).
  • Base32. Uses A-Z and 2-7, so each 5-byte group becomes 8 characters. 60% larger than the input. Case-insensitive, which makes it useful in environments where the case might be lost (some file systems, URLs in some contexts). TOTP secrets, AWS access key IDs, and a few other systems use Base32. The Base32 encoder is on Uttir if you need it.
  • Base64. The 64-character scheme this whole article is about. 33% larger than the input. The default choice for "encode binary as text."

For most cases, Base64 is the right call. Use Base16 (hex) when humans need to read the bytes. Use Base32 when the channel is case-insensitive or the alphabet is restricted. Use Base64 for everything else.

#encoding#developer-tools#tutorial#data-formats

New tools and guides, once a week

One short email when something new ships. No tracking, no images, unsubscribe with one click.