Base64 output is about 33% larger because it stores 6 bits of data in each character, while each input byte holds 8 bits. Three bytes (24 bits) therefore become four characters, a ratio of 4 to 3, and padding can add up to two more characters at the end.
The overhead is a deliberate trade. Base64 uses only 64 printable ASCII characters, so binary data such as images, keys or signatures can travel through systems built for text: email, JSON, HTML attributes and URLs.
The bit-level explanation
RFC 4648 describes the algorithm in one sentence: input is processed in 24-bit groups, and each group is output as four encoded characters. Each character indexes an alphabet of 64 symbols (A–Z, a–z, 0–9, +, /), and 64 = 2⁶, so one character carries exactly 6 bits.
Take the three ASCII letters Man:
| Step | Value |
|---|---|
| Bytes (hex) | 4D 61 6E |
| Bits | 01001101 01100001 01101110 |
| Regrouped in 6s | 010011 010110 000101 101110 |
| Decimal indexes | 19, 22, 5, 46 |
| Base64 characters | T W F u |
Three bytes in, four characters out: TWFu. Since each output character is stored as one byte in ASCII or UTF-8, the encoded data takes 4 bytes for every 3 of the original.
The exact formula
For an input of n bytes, standard padded Base64 produces:
encoded_length = 4 × ceil(n / 3)
The ceil is where padding comes from. If the last group has only one or two bytes, the encoder adds zero bits to fill a 6-bit group and appends = characters so the output length stays a multiple of four.
| Input bytes | Base64 characters | Increase |
|---|---|---|
| 1 | 4 | 300% |
| 2 | 4 | 100% |
| 3 | 4 | 33.3% |
| 100 | 136 | 36.0% |
| 1,024 (1 KiB) | 1,368 | 33.6% |
| 1,048,576 (1 MiB) | 1,398,104 | 33.3% |
The percentages above come from the formula and match what Node.js Buffer.toString("base64") returns for buffers of those sizes. Small values pay the most because of padding, which is why short IDs and tokens often use unpadded base64url.
const base64Length = n => 4 * Math.ceil(n / 3);
base64Length(70); // 96 (a 70-byte PNG becomes 96 characters)
base64Length(30000); // 40000
Padding examples
The RFC 4648 test vectors show padding in action:
| Input | Base64 | Padding |
|---|---|---|
f |
Zg== |
2 characters |
fo |
Zm8= |
1 character |
foo |
Zm9v |
none |
foob |
Zm9vYg== |
2 characters |
foobar |
Zm9vYmFy |
none |
Base64url, the URL-safe variant in RFC 4648 section 5, swaps + and / for - and _. Many formats that use it, including JWT, also drop the = padding, which saves up to two characters per value but does not change the 4:3 ratio.
Going the other way: decoded size
To estimate how many bytes a Base64 string holds, reverse the formula: take the length, multiply by 3/4 and subtract one byte for each = at the end.
function decodedBytes(b64) {
const pad = b64.endsWith("==") ? 2 : b64.endsWith("=") ? 1 : 0;
return (b64.length * 3) / 4 - pad;
}
decodedBytes("SGVsbG8="); // 5 ("Hello")
This is useful when an API enforces a size limit on the decoded file but you only see the encoded text, or when you need to know whether a data URI in a stylesheet is worth keeping. Remove line breaks and other whitespace before you count.
Extra overhead you may not expect
Line breaks in email (MIME)
RFC 2045, the MIME specification, says Base64 output in email must be split into lines of no more than 76 characters. Each line ends with a CRLF pair, which adds two bytes per 76 characters, about 2.6% more. A 1 MiB attachment becomes roughly 1,434,900 bytes of message text instead of 1,048,576.
Data URI prefixes
A data URI adds a small prefix such as data:image/png;base64, (22 characters). That matters only for tiny files, but it is part of why inlining a 70-byte icon costs 118 characters.
Text encodings and escaping
Base64 characters are all ASCII, so they take one byte each in UTF-8. If the text is placed in a format that escapes characters, for example + and / inside a URL query string, the encoded form can grow again (+ becomes %2B). Base64url avoids that.
Does compression help?
Often, yes. Base64 does not add information, so a general-purpose compressor can recover most of the overhead when it sees the encoded text on its own. We ran a quick test with Node.js and gzip at level 9:
| Data | Original | Base64 | Gzip of original | Gzip of Base64 |
|---|---|---|---|---|
| 30,000 random bytes | 30,000 | 40,000 | 30,028 | 30,162 |
| 31,500 bytes of repeated English text | 31,500 | 42,000 | 170 | 223 |
Random bytes represent already-compressed files such as JPEG or PNG. In that case gzip brought the Base64 text back to within about 0.5% of the compressed original. Compression is not free, though: the client still has to decompress and then decode, and the decoded copy lives in memory alongside the text.
When the 33% matters
- Inline images in HTML or CSS. The inline copy is larger, cannot be cached on its own and is downloaded again with every page or stylesheet that contains it. It suits icons of a few hundred bytes, not photos. The image to Base64 tool shows both sizes before you decide.
- APIs that send files in JSON. A 10 MB upload becomes about 13.3 MB of JSON. For large files, a binary upload or a separate file URL is usually better.
- Tokens and headers. A JWT carries base64url JSON; every claim you add grows the header sent with each request. See how to decode a JWT to inspect what yours contains.
- Checksums and hashes. A SHA-256 digest is 32 bytes. It is 64 characters in hex but only 44 in Base64. The Base64 vs hex guide compares the two, and MD5 vs SHA-256 covers the digests themselves.
Summary
Base64 maps 3 bytes to 4 characters because each character holds 6 bits instead of 8. Expect 4 × ceil(n/3) characters, about 33% more for anything but tiny inputs, plus line breaks in email and a prefix in data URIs. Use it when binary data must pass through a text channel, and avoid it for large files that could travel as binary. To encode or decode text quickly, use the Base64 operations on the universal encoder decoder.
Frequently asked questions
Exactly how much bigger is Base64?
The encoded length is 4 × ceil(n ÷ 3) characters for n input bytes. For large inputs that approaches 4/3 of the original, an increase of 33.3%. Small inputs pay more because of padding: 1 byte becomes 4 characters.
Does gzip remove the Base64 overhead?
Mostly, when the encoded text is compressed on its own. In our test, 30,000 random bytes became 40,000 Base64 characters, and gzip reduced that to 30,162 bytes, close to the 30,028 bytes of the gzipped original. Inside a larger file mixed with other content the result varies.
Is base64url smaller than standard Base64?
Only by the padding. Base64url uses - and _ instead of + and /, and protocols such as JWT omit the = padding, saving up to two characters per value. The 4-characters-per-3-bytes ratio is the same.
Turn an image into a data URI, or preview a Base64 string as an image.
Open the Image to Base64