DEV Community

Ken Dsouza
Ken Dsouza

Posted on

What is Base64? A Deep Dive for Developers

Every developer has seen those long strings of random-looking characters — something like SGVsbG8gV29ybGQ= — and wondered what on earth they are. That's Base64 encoding, and once you understand it, you'll start seeing it everywhere: in JWTs, email attachments, CSS stylesheets, API payloads, and more.

Let me break it down properly — not just the "what", but the "how" and "why" too.


So what actually is Base64?

Base64 is a way to represent binary data as plain text.

Computers store everything as binary — images, PDFs, audio files, executables — it's all 1s and 0s underneath. The problem is that many systems (HTTP headers, JSON, email, XML) were designed to handle text only. If you try to shove raw binary data through a text-based system, it gets corrupted. Characters get misinterpreted, null bytes get stripped, line endings get mangled.

Base64 solves this by converting binary data into a safe set of 64 printable ASCII characters:

A-Z  (26 characters)
a-z  (26 characters)
0-9  (10 characters)
+, / ( 2 characters)
─────────────────────
     64 total
Enter fullscreen mode Exit fullscreen mode

That's where the name comes from — it uses exactly 64 characters.


How does the translation actually work?

This is the part most articles skip. Let's go deep.

Step 1 — Everything starts as binary

Take the word "Man". In ASCII:

M = 77  = 01001101
a = 97  = 01100001
n = 110 = 01101110
Enter fullscreen mode Exit fullscreen mode

Put them together as a stream of bits:

01001101 01100001 01101110
Enter fullscreen mode Exit fullscreen mode

Step 2 — Split into 6-bit groups

Normal bytes are 8 bits. Base64 works in 6-bit chunks because 2⁶ = 64 (exactly the size of our character set):

010011  010110  000101  101110
  19      22      5       46
Enter fullscreen mode Exit fullscreen mode

Step 3 — Map each 6-bit value to a character

Base64 has a lookup table (called the Base64 alphabet):

0  = A       16 = Q       32 = g       48 = w
1  = B       17 = R       33 = h       49 = x
2  = C       18 = S       34 = i       50 = y
...
19 = T       22 = W        5 = F       46 = u
Enter fullscreen mode Exit fullscreen mode

So our values 19, 22, 5, 46 map to:

19 → T
22 → W
 5 → F
46 → u
Enter fullscreen mode Exit fullscreen mode

"Man" → TWFu

You can verify this yourself:

btoa("Man") // "TWFu"
Enter fullscreen mode Exit fullscreen mode

What about the = padding?

Base64 works in groups of 3 bytes (24 bits → four 6-bit chunks). If your input isn't divisible by 3, it pads with =:

btoa("M")   // "TQ=="  (1 byte → needs 2 padding chars)
btoa("Ma")  // "TWE="  (2 bytes → needs 1 padding char)
btoa("Man") // "TWFu"  (3 bytes → no padding needed)
Enter fullscreen mode Exit fullscreen mode

The = characters are just filler — they tell the decoder how many real bytes were in the last group.


The size trade-off

Base64 is not free. Encoding increases file size by approximately 33%.

Why? Because you're representing 3 bytes of data using 4 characters:

Original:  3 bytes  = 24 bits
Encoded:   4 chars  = 4 × 6 bits = 24 bits of data
           but each char is stored as 8-bit ASCII
           so: 4 × 8 = 32 bits on disk

Overhead: 32 - 24 = 8 bits extra per 3 bytes ≈ 33% larger
Enter fullscreen mode Exit fullscreen mode

For a 100KB image, the Base64 version will be ~133KB. Keep this in mind when embedding large files.


Real-world use cases with code

1. Embedding images in HTML/CSS

Instead of making an HTTP request for an image file, you can embed it directly:

<!-- Normal image — requires HTTP request -->
<img src="/logo.png" alt="Logo" />

<!-- Base64 embedded — zero HTTP requests -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="Logo" />
Enter fullscreen mode Exit fullscreen mode

Same in CSS:

/* Requires HTTP request */
.icon {
  background-image: url('/icon.png');
}

/* Self-contained, no request needed */
.icon {
  background-image: url('data:image/png;base64,iVBORw0KGgo...');
}
Enter fullscreen mode Exit fullscreen mode

This is useful for small icons, logos, and inline SVGs where eliminating an HTTP round-trip matters more than file size.

2. Sending files through APIs

Most REST APIs communicate in JSON, which is text-only. To send a file:

// Read a file and convert to Base64
const fileToBase64 = (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result.split(',')[1]);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });

// Send it in a JSON payload
const file = document.querySelector('input[type="file"]').files[0];
const base64 = await fileToBase64(file);

await fetch('/api/upload', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    filename: file.name,
    content: base64,
  }),
});
Enter fullscreen mode Exit fullscreen mode

On the server (Node.js), decode it back:

const buffer = Buffer.from(base64String, 'base64');
fs.writeFileSync('output.png', buffer);
Enter fullscreen mode Exit fullscreen mode

3. JWT tokens

If you've used authentication, you've used Base64. A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Enter fullscreen mode Exit fullscreen mode

Each section separated by . is Base64URL encoded (a variant that replaces + with - and / with _ to be URL-safe). You can decode the payload right in your browser:

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";

const payload = token.split('.')[1];

// Base64URL → Base64
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');

console.log(JSON.parse(atob(base64)));
// { sub: "1234567890", name: "John" }
Enter fullscreen mode Exit fullscreen mode

Note: JWTs are encoded, not encrypted. Anyone can decode the payload. Never put sensitive data in a JWT payload unless it's also encrypted.

4. Email attachments (MIME)

This is why Base64 was invented. When you send an email with an attachment, your email client encodes the file as Base64 and wraps it in MIME format:

Enter fullscreen mode Exit fullscreen mode

The email server sees only plain text — no raw binary — so nothing gets corrupted in transit.


Base64 in the browser — btoa and atob

JavaScript has two built-in functions:

// Encode: Binary To Ascii
btoa("Hello World")     // "SGVsbG8gV29ybGQ="

// Decode: Ascii To Binary
atob("SGVsbG8gV29ybGQ=") // "Hello World"
Enter fullscreen mode Exit fullscreen mode

The names are confusing — btoa encodes to Base64, atob decodes from Base64. Just remember: binary to ascii = btoa.

Important caveat: btoa breaks on non-Latin characters:

btoa("Hello 🌍") // ❌ InvalidCharacterError
Enter fullscreen mode Exit fullscreen mode

For Unicode strings, use TextEncoder:

function encodeUnicode(str) {
  const bytes = new TextEncoder().encode(str);
  return btoa(String.fromCharCode(...bytes));
}

function decodeUnicode(str) {
  const bytes = Uint8Array.from(atob(str), c => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

encodeUnicode("Hello 🌍") // works ✅
Enter fullscreen mode Exit fullscreen mode

The privacy problem nobody talks about

Here's something most Base64 articles completely ignore.

Because Base64 looks like gibberish, people assume it's secure. It is absolutely not. It's encoding, not encryption. Anyone can decode it in seconds:

atob("SGVsbG8sIHRoaXMgaXMgbm90IGEgc2VjcmV0")
// "Hello, this is not a secret"
Enter fullscreen mode Exit fullscreen mode

But there's a deeper privacy concern — the tools you use to encode/decode.

Most online Base64 tools work like this:

You select a file
    ↓
File is uploaded to their server
    ↓
Server encodes/decodes it
    ↓
Result sent back to you
    ↓
Your file now lives on someone else's server
Enter fullscreen mode Exit fullscreen mode

For a text string this might not matter. But what about:

  • A confidential PDF document?
  • An image with personal information?
  • A file containing API keys or credentials?

You have no idea what happens to your data once it hits that server. It could be logged, stored, analyzed, or breached.

The correct way to handle this is entirely browser-based processing. The Web File API and JavaScript are powerful enough to encode and decode any file locally — no server needed:

// This runs entirely in YOUR browser
// Nothing leaves your device
const reader = new FileReader();
reader.onload = (e) => {
  const base64 = e.target.result.split(',')[1];
  console.log(base64); // encoded, locally
};
reader.readAsDataURL(file);
Enter fullscreen mode Exit fullscreen mode

I built base64convertor.com (https://base64convertor.com) specifically for this reason — 60+ Base64 tools that run entirely in your browser. No uploads, no server, no logs. Your files never leave your device.


Base64 vs Base64URL

Quick distinction worth knowing:

                Base64          Base64URL
Characters:     A-Z a-z 0-9    A-Z a-z 0-9
                + /             - _
Padding:        =               Optional
Safe in URLs:   No              Yes
Used in:        Files, emails   JWTs, OAuth,
                CSS             URL params
Enter fullscreen mode Exit fullscreen mode
// Base64URL encode
const base64url = btoa(str)
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=/g, '');

// Base64URL decode
const base64 = base64url
  .replace(/-/g, '+')
  .replace(/_/g, '/');
const decoded = atob(base64);
Enter fullscreen mode Exit fullscreen mode

Quick reference

// Encode string
btoa("hello")                        // "aGVsbG8="

// Decode string
atob("aGVsbG8=")                     // "hello"

// Encode Unicode string
const bytes = new TextEncoder().encode("héllo");
btoa(String.fromCharCode(...bytes))  // "aMOpbGxv"

// Encode file (browser)
reader.readAsDataURL(file)            // "data:image/png;base64,..."

// Decode to Blob (browser)
const bytes = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
const blob = new Blob([bytes], { type: 'image/png' });

// Encode in Node.js
Buffer.from("hello").toString('base64')         // "aGVsbG8="

// Decode in Node.js
Buffer.from("aGVsbG8=", 'base64').toString()    // "hello"
Enter fullscreen mode Exit fullscreen mode

Wrapping up

Base64 is one of those foundational concepts that shows up constantly in web development — APIs, auth tokens, emails, CSS, file handling. Understanding how it works at the bit level makes it far less mysterious.

Key takeaways:

  • Base64 converts binary → 64 printable ASCII characters
  • It works in 3-byte input → 4-character output chunks
  • Output is ~33% larger than input
  • It is encoding, not encryption — never use it for security
  • Browser-based tools are always safer than uploading files to unknown servers

If you need to encode or decode Base64 — images, PDFs, audio, video, documents — base64convertor.com (https://base64convertor.com) does it all, privately, right in your browser.


Have questions or something to add? Drop a comment below.

— Ken D'Souza


Tags: #webdev #javascript #tutorial #security

Top comments (0)