DEV Community

楊東霖
楊東霖

Posted on • Originally published at devplaybook.cc

Base64 Encode/Decode Explained: When and How to Use It

Base64 is one of those things every developer uses but few fully understand. You've seen it in JWT tokens, CSS data URIs, email attachments, and API responses. You know it makes binary data look like random letters, but when should you actually use it — and when is it the wrong tool?

This guide explains the Base64 algorithm clearly, covers every practical use case, shows how to encode and decode in the most common languages, and points out the mistakes developers make when reaching for Base64 out of habit.


What Is Base64?

Base64 is a binary-to-text encoding scheme. It converts binary data (bytes) into a string of printable ASCII characters using a 64-character alphabet:

  • Uppercase letters: A–Z (26 characters)
  • Lowercase letters: a–z (26 characters)
  • Digits: 0–9 (10 characters)
  • Two additional characters: + and /
  • Padding character: =

That's 64 characters — hence the name.

Why 64?

Because every ASCII character that's "safe" to transmit through text-only systems is representable with a small, stable set. Original email systems, HTTP headers, and many protocols were designed for ASCII text, not arbitrary bytes. Base64 bridges the gap: take any binary data, turn it into a safe text string.

How It Works

Base64 processes input in 3-byte groups and converts each group into 4 Base64 characters.

  1. Take 3 bytes (24 bits)
  2. Split into four 6-bit groups
  3. Map each 6-bit group to one Base64 character using a lookup table

Example: encoding Man

ASCII values: M=77, a=97, n=110
Binary:       01001101 01100001 01101110

Split into 6-bit groups:
010011 | 010110 | 000101 | 101110

Map to Base64 alphabet:
010011 = 19 = T
010110 = 22 = W
000101 = 5  = F
101110 = 46 = u

Result: TWFu
Enter fullscreen mode Exit fullscreen mode

If the input isn't a multiple of 3 bytes, padding (=) fills the output to a multiple of 4 characters.


Base64 Is Not Encryption

This is the most important thing to understand: Base64 encoding is not encryption and provides no security.

Encoding: reversible transformation, no key required. Anyone can decode it.
Encryption: transformation that requires a key to reverse. Without the key, the data is unreadable.

If you Base64 encode a password before storing it, that password is exposed. If you Base64 encode a credit card number in an API response, that number is exposed to anyone who intercepts the response.

Base64 is for format transformation, not protection. Use it to make binary data text-safe. Use proper encryption (AES-256, RSA, etc.) when you need to protect data.


When to Use Base64

Base64 is the right choice in these situations:

1. Embedding Images in CSS or HTML

Data URIs let you embed small images directly in your CSS without a separate HTTP request:

.icon {
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABmJLR0QA/wD/AP+gvaeTAAAADklEQVQI12NgQAIABQABNjN9GQAAAALaA==');
}
Enter fullscreen mode Exit fullscreen mode

This is useful for small icons (under 10KB) where the request overhead would cost more than the encoding overhead. For larger images, always use separate files — the 33% size increase and browser caching limitations make data URIs counterproductive.

2. Encoding Binary Data in JSON APIs

JSON only supports Unicode strings. If your API needs to return binary data (an image thumbnail, a cryptographic signature, raw bytes), you have two options: Base64 encode it, or use a binary-safe format like MessagePack.

{
  "filename": "avatar.png",
  "data": "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs...",
  "encoding": "base64"
}
Enter fullscreen mode Exit fullscreen mode

Always include an encoding field to make it explicit that the value is Base64 encoded.

3. HTTP Basic Authentication

The Authorization: Basic header encodes credentials as Base64(username:password):

Authorization: Basic dXNlcjpwYXNzd29yZA==
Enter fullscreen mode Exit fullscreen mode

Decoded: user:password

Important: this is only "safe" because HTTPS encrypts the entire HTTP exchange. Over plain HTTP, Basic Auth is trivially intercepted. The Base64 encoding is not security — the TLS layer is.

4. JWT Tokens

JWT tokens use Base64url encoding (a URL-safe variant) for the header and payload:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzExMTQ0ODAwfQ.signature
Enter fullscreen mode Exit fullscreen mode

Decoded header: {"alg":"HS256","typ":"JWT"}
Decoded payload: {"sub":"user123","exp":1711144800}

The payload is Base64url encoded, not encrypted. Any JWT can be decoded and the payload read without the signing secret. Never put sensitive data (passwords, SSNs, private keys) in a JWT payload unless the token is also encrypted (JWE, not just JWS).

5. Email Attachments (MIME)

Email protocols were designed for ASCII text. Binary attachments (PDFs, images, Office documents) are Base64 encoded in MIME messages. You see this in raw email source or when parsing email programmatically.

6. Cryptographic Data Exchange

Public keys, certificates, and signatures are often distributed in PEM format, which is Base64 encoded DER data wrapped in header lines:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----
Enter fullscreen mode Exit fullscreen mode

When NOT to Use Base64

For Large Binary Files

A 1MB file becomes ~1.33MB Base64 encoded. For file upload APIs, use multipart/form-data instead:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
Enter fullscreen mode Exit fullscreen mode

This streams the binary directly without the 33% overhead.

For URLs

Standard Base64 uses +, /, and = — characters that have special meaning in URLs. If you need to put Base64 in a URL, use Base64url instead (- instead of +, _ instead of /, no padding).

Never URL-encode standard Base64 to put it in a URL — the double encoding creates maintenance headaches.

As "Obfuscation"

Encoding a password in Base64 and calling it "encrypted" is a security vulnerability. It is not obfuscation — it's a well-known, standardized format. Security auditors check for this specifically.


How to Encode and Decode Base64

Browser (JavaScript)

The browser provides btoa() and atob() for ASCII strings:

// Encode
const encoded = btoa('Hello, World!');
// "SGVsbG8sIFdvcmxkIQ=="

// Decode
const decoded = atob('SGVsbG8sIFdvcmxkIQ==');
// "Hello, World!"
Enter fullscreen mode Exit fullscreen mode

For Unicode strings (anything outside ASCII), you need to handle the encoding manually:

// Encode Unicode string to Base64
function encodeUnicode(str) {
  return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, p1) =>
    String.fromCharCode('0x' + p1)
  ));
}

// Decode Base64 to Unicode string
function decodeUnicode(str) {
  return decodeURIComponent(atob(str).split('').map(c =>
    '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
  ).join(''));
}

encodeUnicode('日本語');
// "5pel5pys6Kqe"
Enter fullscreen mode Exit fullscreen mode

Node.js

Node.js uses Buffer for Base64 operations:

// Encode string to Base64
const encoded = Buffer.from('Hello, World!').toString('base64');
// "SGVsbG8sIFdvcmxkIQ=="

// Decode Base64 to string
const decoded = Buffer.from('SGVsbG8sIFdvcmxkIQ==', 'base64').toString('utf8');
// "Hello, World!"

// Encode binary file to Base64
const fs = require('fs');
const imageData = fs.readFileSync('image.png');
const imageBase64 = imageData.toString('base64');

// Base64url (no padding, URL-safe characters)
const urlSafe = Buffer.from('Hello+World/Test=').toString('base64url');
Enter fullscreen mode Exit fullscreen mode

Python

import base64

# Encode bytes to Base64
encoded = base64.b64encode(b'Hello, World!')
# b'SGVsbG8sIFdvcmxkIQ=='

# Decode Base64 to bytes
decoded = base64.b64decode('SGVsbG8sIFdvcmxkIQ==')
# b'Hello, World!'

# String encoding: encode the string to bytes first
text = 'Hello, World!'
encoded_str = base64.b64encode(text.encode('utf-8')).decode('utf-8')
# 'SGVsbG8sIFdvcmxkIQ=='

# Base64url (URL-safe variant)
url_safe = base64.urlsafe_b64encode(b'Hello+World')
url_decoded = base64.urlsafe_b64decode(url_safe)

# Encode a file
with open('image.png', 'rb') as f:
    image_base64 = base64.b64encode(f.read()).decode('utf-8')
Enter fullscreen mode Exit fullscreen mode

Go

import (
    "encoding/base64"
    "fmt"
)

// Encode
encoded := base64.StdEncoding.EncodeToString([]byte("Hello, World!"))
fmt.Println(encoded) // SGVsbG8sIFdvcmxkIQ==

// Decode
decoded, err := base64.StdEncoding.DecodeString("SGVsbG8sIFdvcmxkIQ==")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(decoded)) // Hello, World!

// URL-safe Base64 (for JWTs, URLs)
urlEncoded := base64.URLEncoding.EncodeToString([]byte("Hello, World!"))
urlDecoded, _ := base64.URLEncoding.DecodeString(urlEncoded)

// URL-safe without padding
rawEncoded := base64.RawURLEncoding.EncodeToString([]byte("Hello, World!"))
Enter fullscreen mode Exit fullscreen mode

Rust

use base64::{Engine, engine::general_purpose};

// Encode
let encoded = general_purpose::STANDARD.encode(b"Hello, World!");
println!("{}", encoded); // SGVsbG8sIFdvcmxkIQ==

// Decode
let decoded_bytes = general_purpose::STANDARD.decode("SGVsbG8sIFdvcmxkIQ==").unwrap();
let decoded = String::from_utf8(decoded_bytes).unwrap();
println!("{}", decoded); // Hello, World!

// URL-safe (no padding)
let url_safe = general_purpose::URL_SAFE_NO_PAD.encode(b"Hello, World!");
Enter fullscreen mode Exit fullscreen mode

Bash / Command Line

# Encode
echo -n "Hello, World!" | base64
# SGVsbG8sIFdvcmxkIQ==

# Decode
echo "SGVsbG8sIFdvcmxkIQ==" | base64 -d
# Hello, World!

# Encode a file
base64 image.png > image.b64

# Decode a file
base64 -d image.b64 > image.png
Enter fullscreen mode Exit fullscreen mode

Base64 vs Base64url: Which to Use?

Feature Base64 (Standard) Base64url
Characters A-Z, a-z, 0-9, +, / A-Z, a-z, 0-9, -, _
Padding Required (=) Optional
URL-safe No (+, / need escaping) Yes
Used in MIME email, PEM files, data URIs JWTs, OAuth, URL parameters

Use standard Base64 for: email, PEM/certificate files, data URIs, HTTP Basic Auth headers.

Use Base64url for: anything appearing in a URL, JWT tokens, OAuth state parameters, filename-safe identifiers.


Performance: How Much Does Base64 Increase Size?

Base64 encodes 3 bytes into 4 characters — a 33% size increase. This matters at scale:

Original Size Base64 Size Increase
1 KB ~1.37 KB +370 bytes
10 KB ~13.7 KB +3.7 KB
100 KB ~137 KB +37 KB
1 MB ~1.37 MB +370 KB

Impact on HTTP Requests

When you embed Base64-encoded images in CSS, you remove an HTTP request but increase the CSS file size. The tradeoff:

  • Worth it: Small icons under 5-10KB where the HTTP request overhead (TCP connection, headers, DNS, latency) exceeds the size cost
  • Not worth it: Photos, illustrations, or any image over ~10KB — the size increase outweighs the request savings

Modern HTTP/2 connection multiplexing reduces the "too many requests" problem significantly. With HTTP/2, multiple resources are served over one connection, making the "save a request" argument for Base64 data URIs weaker than it was in the HTTP/1.1 era.

Impact on JavaScript Bundles

Bundlers (webpack, Rollup, esbuild) can inline small assets as Base64 data URIs automatically:

// webpack config — inline assets under 8KB as Base64
module.exports = {
  module: {
    rules: [{
      test: /\.(png|jpg|gif|svg)$/,
      type: 'asset',
      parser: {
        dataUrlCondition: {
          maxSize: 8 * 1024 // 8KB
        }
      }
    }]
  }
};
Enter fullscreen mode Exit fullscreen mode

For Vite:

// vite.config.js
export default {
  build: {
    assetsInlineLimit: 4096 // 4KB — inline as Base64 if smaller
  }
};
Enter fullscreen mode Exit fullscreen mode

These settings handle the tradeoff automatically based on file size.

Using DevPlaybook Base64 Tool

For quick encoding and decoding without writing code, DevPlaybook's Base64 tool handles:

  • Text strings (including Unicode)
  • File uploads (encode small files to Base64)
  • Paste-and-decode for instant inspection
  • Base64url variant support
  • Copy-to-clipboard output

Useful scenarios: inspecting a JWT payload, checking what an API is returning in a Base64 field, encoding a small image for a CSS data URI, or verifying that your encoding implementation matches expected output.


Debugging Base64 Issues

"Invalid character in Base64 string"

You're using standard Base64 where Base64url is expected (or vice versa). Check for +/- and //_ characters.

Output doesn't match expected value

Check the input encoding. btoa('hello') in JavaScript operates on Latin-1 characters. If your string contains characters above U+00FF, you'll get a DOMException. Use the Unicode-safe wrapper shown earlier.

Decoding produces garbled text

The Base64 might be correct, but you're decoding as the wrong character encoding. If the original was UTF-8, decode as UTF-8:

const text = new TextDecoder('utf-8').decode(
  Uint8Array.from(atob(encoded), c => c.charCodeAt(0))
);
Enter fullscreen mode Exit fullscreen mode

Missing padding errors

Base64url often omits the = padding. Before decoding standard Base64, add padding:

function addPadding(base64) {
  return base64 + '='.repeat((4 - base64.length % 4) % 4);
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Base64 is a practical, widely-used encoding that solves a specific problem: making binary data safe to transmit through text-only channels. Use it for embedding images in CSS, encoding binary data in JSON APIs, HTTP Basic Auth credentials, JWT tokens, and PEM files. Avoid it for large files (use multipart instead), URL parameters (use Base64url), and anything where you need actual security (use encryption).

For quick encode/decode operations, DevPlaybook's Base64 tool handles any input in the browser without sending data anywhere. For programmatic use, every major language has built-in support — use the examples above as a reference.


Working with JWT tokens that contain Base64url-encoded payloads? The JWT Decoder shows the full decoded structure at a glance. Formatting the JSON output? Try the JSON Formatter.


Level Up Your Dev Workflow

Found this useful? Explore DevPlaybook — cheat sheets, tool comparisons, and hands-on guides for modern developers.

🛒 Get the DevToolkit Starter Kit on Gumroad — 40+ browser-based dev tools, source code + deployment guide included.

Top comments (0)