Base64 takes three bytes and produces four characters. That is a 33 per cent increase in size with no data added, and it is the entire point: the output uses only characters that survive being sent through systems built for text.
It is not compression and it is not encryption. It is a way of writing arbitrary bytes down using a small, safe alphabet, and anyone can reverse it without a key.
Where does the third come from?
From the bit arithmetic. Three bytes is 24 bits; 24 bits split into six-bit groups is four groups; each group indexes a 64-character alphabet. Four characters out for every three bytes in, so the output is four thirds of the input.
| Input bytes | Output characters | Padding |
|---|---|---|
| 3 | 4 | none |
| 2 | 4 | one = |
| 1 | 4 | two == |
The padding exists because the output is always a multiple of four characters. One leftover byte still needs two characters to carry its eight bits, and the equals signs fill the rest so a decoder knows where the real data stopped.
The alphabet is the twenty-six capitals, the twenty-six lowercase letters, the ten digits, and two more — which is why the last two vary between variants.
Why are there two alphabets?
Because the standard alphabet ends in plus and slash, and both mean something in a URL. A slash is a path separator and a plus has historically meant a space in form data, so base64 in a query string arrives corrupted.
The URL-safe variant swaps them for hyphen and underscore, which are safe everywhere in a URL. The padding is often dropped too, since the length alone determines how much padding there was.
This is the variant JSON Web Tokens use, and it is why a token contains hyphens and underscores where an email attachment contains plus signs and slashes.
Is a JWT encrypted?
No, and this is the most consequential misunderstanding in the family. A token is three base64url segments joined by dots — a header, a payload and a signature — and the payload is encoded, not encrypted.
Anyone holding the token can read every claim in it. Decoding takes no key and no permission, which means a token is a bad place for anything private and a fine place for a user identifier and an expiry.
The signature is the part that matters, and it proves only that the token has not been altered since it was issued. Decoding a token tells you what it says; it does not tell you whether it is valid, and treating a successful decode as a successful check is the bug that follows from the confusion.
Signature verification needs the key the token was signed with, which a decoder in a browser does not have and should not be given. Reading a token is a debugging step; checking one belongs on the server that issued it.
What does percent-encoding do differently?
It escapes selectively rather than transforming everything. Base64 rewrites all the input; percent-encoding leaves the safe characters alone and replaces the rest with a percent sign and two hex digits.
The trap is that there are two correct answers depending on scope. Encoding a whole URL must leave the slashes and the question mark intact, or the structure is destroyed. Encoding one value to go inside a URL must escape them, or the value breaks out of its parameter.
Spaces have their own inconsistency: percent-encoded they become %20, and in a form-encoded body they historically become a plus sign. Both are correct in their own context, which is why a plus sign in a search box sometimes arrives as a space.
When do you escape for HTML?
Whenever text goes into a page. Five characters carry meaning in markup — the angle brackets, the ampersand and the two quote marks — and any of them arriving unescaped in text can end the element it was supposed to sit inside.
The ampersand is the one people forget, and it matters because escaping is not idempotent if you get it wrong: escaping already-escaped text turns an entity into visible characters, so double-escaped output shows the entity itself on the page.
Regular expressions have the same problem with a different character set. A dot, a plus, brackets and parentheses all mean something to a pattern, so a literal string used as a pattern needs its metacharacters escaped first.
Questions people ask
Why does my decoded text end in odd characters? The input was truncated. Base64 decodes in four-character groups, so a missing character corrupts the tail rather than erroring.
Can base64 be used to hide data? It obscures it from a glance and nothing more. Reversal takes one step and no secret.
Why are images sometimes embedded as base64? A data URI puts the bytes inside the document, saving a request at the cost of a third more size and no caching.
Does base64 change with encoding? It encodes bytes, so text must be converted to bytes first. The same text in two encodings gives two different results.
Is the output safe in a filename? Not the standard alphabet — the slash is a path separator. Use the URL-safe variant.
Encode for the channel you are using. Base64 encode and decode handle the byte-to-text conversion, the URL encoder and HTML encode do the selective kind, the regex escaper makes a literal string safe as a pattern, and the JWT decoder reads a token without pretending to verify it.