{ }jsonkitOpen

Base64 encoding explained: format, examples and URL-safe Base64

8 min read · updated 2026-07-12

Base64 encoding turns binary data into plain ASCII text so it can pass through systems that only expect text. You see it in data: URLs that embed an image, in JWT payloads, in email attachments and in API keys stored in config files.

This guide explains Base64 from the ground up. It covers the problem Base64 solves, the algorithm that turns three bytes into four characters, the standard and URL-safe alphabets from RFC 4648, and what the = padding is for. Most important of all: Base64 is an encoding, not encryption. It hides nothing.

The problem Base64 solves

Computers store everything as bytes. A byte is 8 bits and holds a value from 0 to 255. Text is one way to interpret those bytes. Many systems were built to carry *text*: email bodies, URLs, JSON strings, XML documents, HTTP headers. Give them raw binary, like the bytes of a PNG, a compressed archive or an encryption key, and things break. A null byte can cut a string short. A byte that matches a control character can get mangled. Anything outside printable ASCII may not survive the trip.

Base64 avoids the problem by writing binary data with only 64 characters that every text system accepts: A-Z, a-z, 0-9 and two symbols. The result looks like gibberish, but it is always safe printable text. You can put it in a JSON value, a URL or an email, and it arrives with every byte intact.

How Base64 works, with an example

The name describes the algorithm: work in base 64. Read the input as a stream of bits and regroup it. Binary uses 8 bits per byte. Base64 uses 6 bits per character, because 2^6 = 64. The least common multiple of 8 and 6 is 24, so Base64 reads 3 bytes (24 bits) at a time and writes 4 characters (24 bits).

Here is a Base64 example with the word Man. Its three ASCII bytes are 77, 97 and 110, which in binary is 01001101 01100001 01101110. Line up those 24 bits and cut them into four 6-bit groups:

Encoding 'Man': 3 bytes become the 4 characters 'TWFu'.text
Text:     M          a          n
ASCII:    77         97         110
Bits:     01001101   01100001   01101110
Regroup:  010011  010110  000101  101110
Value:    19      22      5       46
Base64:   T       W       F       u

Each 6-bit value, from 0 to 63, is an index into the Base64 alphabet. Index 19 is T, 22 is W, 5 is F and 46 is u, so Man encodes to TWFu. Decoding reverses the process. Map each character back to its 6-bit value, join the bits, and cut them into 8-bit bytes.

Base64 in JavaScript, and the UTF-8 caveat for non-ASCII text.js
// In the browser (and modern Node):
const encoded = btoa("Man"); // "TWFu"
const decoded = atob("TWFu"); // "Man"

// Note: btoa/atob work on binary strings, not Unicode text.
// For arbitrary strings, encode to UTF-8 bytes first:
const bytes = new TextEncoder().encode("café");
const b64 = btoa(String.fromCharCode(...bytes)); // "Y2Fmw6k="

Padding: what the '=' means

Base64 works in 3-byte groups, but real data rarely divides evenly by three. When the last group has only 1 or 2 bytes, the encoder pads the output to a multiple of four characters with the = sign. = is never a data character. It only tells the decoder how many bytes the last group held.

  • 3 bytes left → 4 characters, no padding (for example TWFu).
  • 2 bytes left → 3 characters + one = (for example TWE= for Ma).
  • 1 byte left → 2 characters + two = (for example TQ== for M).

The number of = signs tells the decoder how many trailing bytes to keep. Some formats drop the padding to save characters, most notably JWTs and URL parameters. A decoder can usually work out the length without it, which is why you will see unpadded Base64 in practice.

Standard vs URL-safe Base64 (RFC 4648)

The Base64 format is defined in RFC 4648, which specifies two alphabets. The first 62 characters are the same in both: A-Z, a-z, 0-9. They differ only in the last two symbols and the padding, and that difference matters a lot depending on where the string goes.

Index 62Index 63PaddingSafe in URLs?
Standard Base64+/=No
URL-safe Base64-_often omittedYes

Standard Base64 uses + and /, and both cause trouble in a URL. Query strings read + as a space, and / separates path segments. URL-safe Base64 (also called base64url) swaps them for - (hyphen) and _ (underscore), which need no escaping in a URL or filename. The = sign is also reserved in URLs, so URL-safe Base64 usually drops the padding.

Converting between the two is a character swap. Replace + with - and / with _, and strip =, to go URL-safe. Reverse it to go back. If a decoder rejects a string, check for a mismatched alphabet first. The tool below encodes either variant and decodes both.

Base64 Encode / DecodeOpen the tool→

Base64 is not encryption

This is the most common misconception, and it has real security consequences. Base64 is a reversible, public transformation with no key. Anyone can decode any Base64 string, because no secret is involved. It provides no confidentiality. Encoding a password, API secret or personal data in Base64 does *not* protect it. It only makes it a little less obvious to someone skimming the text.

Base64 offers no secrecy. Decoding needs no key.js
// "Encoding" a secret protects nothing:
const secret = btoa("hunter2"); // "aHVudGVyMg=="
atob("aHVudGVyMg=="); // "hunter2", anyone can reverse it

Encoding changes how data is represented so systems can handle it (Base64, URL encoding, UTF-8). Encryption protects data so only key holders can read it (AES, RSA). If you need confidentiality, you need encryption. Use Base64 for what it is for: moving binary safely through text channels.

Where you will meet Base64

  • Data URLs. data:image/png;base64,iVBORw0KGgo... embeds an image directly in HTML or CSS, with no separate request.
  • JWTs. The header and payload are base64url-encoded JSON. See the JWT guide.
  • Email (MIME). Attachments and non-ASCII bodies are Base64-encoded so they survive text-only mail transport.
  • HTTP Basic Auth. The Authorization header carries base64(username:password), which is why Basic Auth needs HTTPS.
  • Binary in JSON. JSON has no binary type, so a byte blob is stored as a Base64 string field.

JSON developers hit that last case most often. JSON has no binary type, so any file bytes, hash or key you want in a payload has to become a Base64 string first and be decoded on the other side.

To inspect a token, embed an asset or check what a Base64 string contains, decode it locally. The tool below runs in your browser and uploads nothing, so you can paste a token without sending sensitive data anywhere.

Base64 Encode / DecodeOpen the tool→

Frequently asked questions

Is Base64 encryption or encoding?

Encoding. Base64 is a public, reversible transformation with no key, so anyone can decode any Base64 string. It changes how data is represented so binary can pass through text channels, but it provides no confidentiality. For secrecy you need real encryption like AES. Never rely on Base64 to protect a password, token or personal data.

Why is Base64-encoded data about 33% larger?

Base64 uses one 8-bit character for every 6 bits of input. It processes data in 3-byte (24-bit) groups that become 4 characters. Four characters for every three bytes is a 4:3 ratio, so the output is about 33% bigger than the original, before padding.

What is the difference between standard and URL-safe Base64?

Both share the first 62 characters (A-Z, a-z, 0-9). Standard Base64 (RFC 4648 section 4) uses + for index 62 and / for index 63, with = padding. URL-safe Base64 (section 5) uses - and _ instead, so the string is safe in URLs and filenames, and it usually omits the = padding. JWTs use the URL-safe variant.

What does the '=' at the end of a Base64 string mean?

It is padding. Base64 works in 3-byte groups. When the last group has only 1 or 2 bytes, the encoder pads the output with = so the length stays a multiple of four. Two = signs mean one leftover byte, and one = means two leftover bytes. Some formats, like JWTs and URLs, drop the padding because the length can be inferred.

Why can't I put raw binary directly in JSON?

JSON has only six value types (string, number, boolean, null, object and array) and no binary type. Raw bytes can also contain characters that break string parsing. The standard workaround is to Base64-encode the bytes into a JSON string and decode them on the other side.

Does btoa in JavaScript handle Unicode text?

Not directly. btoa expects a 'binary string' where each character is a single byte (0 to 255), so it throws on characters above U+00FF, like emoji or many accented letters. Encode the text to UTF-8 bytes first, for example with TextEncoder, then Base64 those bytes. Reverse the steps when decoding.