The formula
For an input of n bytes, padded Base64 (the default in RFC 4648) is exactly:
length = 4 × ceil(n / 3)
Without padding, as in Base64URL for JWTs, it is:
length = ceil(4n / 3)
The two differ by the number of = signs, which is 0, 1 or 2 depending on n mod 3. The padding guide explains where they come from.
Going the other way, a padded Base64 string of length L decodes to:
bytes = 3 × L / 4 − (number of trailing "=")
So TWFu is 3 bytes, TWE= is 2 and TQ== is 1. Strip whitespace and line breaks before counting. The Base64 size calculator does both directions, including line breaks and data URI prefixes.
In code:
import base64, math, os
for n in [1, 2, 3, 100, 1000]:
data = os.urandom(n)
assert len(base64.b64encode(data)) == 4 * math.ceil(n / 3)
const b64Len = (n) => 4 * Math.ceil(n / 3);
const rawLen = (s) => (s.length * 3) / 4 - (s.endsWith("==") ? 2 : s.endsWith("=") ? 1 : 0);
b64Len(1000); // 1336
rawLen("TWE="); // 2
Why the overhead is 33%
A byte has 8 bits. A Base64 character carries 6 bits of data, because it is one of 64 = 2⁶ symbols, but it is stored as an 8-bit ASCII byte. So each output byte holds 6 useful bits, and the output must be 8/6 = 4/3 of the input. That is 33.3% extra, and it is a hard floor: no Base64 implementation can do better without abandoning the alphabet. (How the bits are regrouped is shown step by step in how Base64 works.)
For small inputs, padding makes the ratio worse, since output always comes in blocks of 4. One byte becomes 4 characters, a 300% increase. By a few hundred bytes the padding is negligible and the ratio settles at 4/3.
Denser text encodings exist. Base85 stores 4 bytes in 5 characters (25% overhead), and Base91 gets slightly lower. They are less widely supported and their alphabets include characters that cause trouble in some contexts. The comparison is in Base64 vs hex, Base32, Base58, Base85.
Line breaks: MIME, PEM and command-line tools
Some formats wrap Base64 into lines, and the line endings count toward the size.
| Format | Line length | Line ending | Total overhead |
|---|---|---|---|
| Plain Base64 (RFC 4648) | no wrapping | none | 33.3% |
| MIME email (RFC 2045) | 76 | CRLF (2 bytes) | 36.8% |
GNU base64, Python encodebytes | 76 | LF (1 byte) | 35.1% |
| PEM certificates and keys | 64 | LF | 35.4% |
The MIME figure comes from 4/3 × 78/76: every 76 characters of Base64 bring 2 more bytes of CRLF. With L = 4 × ceil(n / 3), the size including a CRLF after every line, the last one too, is:
mime_length = L + 2 × ceil(L / 76)
Each full 76-character line holds exactly 57 input bytes, which is why MIME encoders process data in 57-byte chunks. More on email in Base64 in email, and the MIME Base64 encoder produces wrapped output.
Size table
Computed with the formulas above and checked against Python’s base64 module.
| Input | Padded Base64 | Unpadded | MIME (76 + CRLF) | Overhead (padded) |
|---|---|---|---|---|
| 1 byte | 4 | 2 | 6 | 300% |
| 2 bytes | 4 | 3 | 6 | 100% |
| 3 bytes | 4 | 4 | 6 | 33.3% |
| 10 bytes | 16 | 14 | 18 | 60% |
| 57 bytes | 76 | 76 | 78 | 33.3% |
| 100 bytes | 136 | 134 | 140 | 36% |
| 1,000 bytes | 1,336 | 1,334 | 1,372 | 33.6% |
| 1 KiB (1,024) | 1,368 | 1,366 | 1,404 | 33.6% |
| 1 MB (10⁶) | 1,333,336 | 1,333,334 | 1,368,424 | 33.3% |
| 1 MiB (1,048,576) | 1,398,104 | 1,398,102 | 1,434,898 | 33.3% |
| 10 MiB | 13,981,016 | 13,981,014 | 14,348,938 | 33.3% |
| 25 MB (25 × 10⁶) | 33,333,336 | 33,333,334 | 34,210,530 | 33.3% |
The last row is the email case. SMTP size limits (the SIZE extension, RFC 1870) count the message as transmitted, encoding included, so a 25 MB file becomes about 34.2 MB on the wire and will not pass a 25 MB server limit. Some providers describe their limits in terms of the attachment instead, so check how yours counts.
What compression does
Compression interacts with Base64 in two ways, and the order matters.
Compress before encoding whenever you control both ends. Base64 of compressed data is 4/3 of a smaller number. For the GPL-3 license text (35,149 bytes) we measured with Node.js zlib at gzip level 9:
| Pipeline | Size |
|---|---|
| Raw text | 35,149 |
| Base64 only | 46,868 |
| Base64, then gzip | 17,465 |
| gzip, then Base64 | 16,124 |
| gzip only | 12,091 |
The Gzip + Base64 tool does the “gzip, then Base64” pipeline in the browser.
Transport compression recovers most of the overhead for binary data. If a Base64 string travels inside an HTTP response compressed with gzip or Brotli, the compressor squeezes the 6-bit characters back toward 6 bits each. For 1 MiB of random bytes, Base64 grew to 1,398,104 characters; gzipped it was 1,053,023 bytes (0.4% above the raw data) and Brotli brought it to 1,048,805. The same data with MIME line breaks gzipped to 1,088,689, so the CRLFs cost about 3.4% even after compression.
Text is different. Base64 breaks the repetition that compressors depend on, so Base64 text compresses much worse than the text itself: 17,465 against 12,091 bytes in the table above. The data URI performance article has measurements for PNG, JPEG, WOFF2 and SVG.
URL, header and cookie limits
Base64 inflates anything you put in a URL, header or cookie, and all three have limits.
- URLs. RFC 9110 recommends that senders and recipients support URIs of at least 8,000 octets. Many servers stop near there by default: nginx allocates header buffers of 8 KB for the request line, and Apache’s
LimitRequestLinedefaults to 8,190 bytes. Longer URLs typically fail with414 URI Too Long. With Base64URL that is room for roughly 6,000 bytes of data, minus the rest of the URL. - Headers. The same server buffers apply to each header line, which matters for long JWTs in
Authorization: Bearer ...headers and for large Basic Auth credentials. - Cookies. RFC 6265 asks browsers to support at least 4,096 bytes per cookie, including name and attributes. That is about 3 KB of data once Base64 encoded.
If a Base64 payload is approaching any of these limits, send it in a request body instead, or store it server-side and pass an ID.
Enter a byte count or paste a string to see encoded and decoded sizes, with or without line breaks.
FAQ
How much bigger is Base64 than the original file?
About 33.3% for anything larger than a few bytes. With MIME line breaks (76 characters plus CRLF) it is about 36.8%.
How do I calculate the original size from a Base64 string?
Take the length without whitespace, multiply by 3/4, and subtract the number of = signs at the end. For example, TWE= is 4 × 3/4 − 1 = 2 bytes.
Why does a 1-byte input produce 4 characters?
Base64 always outputs complete 4-character blocks when padding is used. One byte fills 2 characters and the other 2 are =, so tiny inputs have a much higher relative overhead.
Can I make Base64 smaller?
Compress the data before encoding it, for example with gzip, and drop the padding if the receiver allows it. Base64 itself has a fixed ratio; denser text encodings like Base85 exist but are less widely supported.