Encoding is not encryption. It sounds obvious and it is the single most common misunderstanding behind real security incidents in this area.
What Base64 is for
Base64 turns arbitrary bytes into 64 safe ASCII characters. It exists because a lot of the internet was built for text and breaks on raw binary — email bodies, JSON string fields, XML, URLs.
The cost is size: three bytes become four characters, so everything grows by about 33%. That overhead is the reason email attachments inflate, and why a 20 MB file bounces off a 25 MB limit.
- Legitimate uses: embedding a small image as a data URI, attaching a binary to an email, putting a file in a JSON field, storing a key in an environment variable.
- Not a use: hiding anything. Decoding takes one command and no key.
- Not compression: it makes data larger, never smaller.
- Watch the variants: standard Base64 uses
+and/, which are unsafe in URLs. Base64url substitutes-and_. Feeding one to a decoder expecting the other fails.
A useful rule: if a value is Base64 and it is sensitive, it is exposed. Treat encoded credentials in a config file exactly as you would treat plaintext ones.
URL encoding: what to escape and where
A URL has a grammar. Certain characters mean something structural — ? starts the query, & separates parameters, # starts the fragment, / separates path segments. Any of those appearing in data has to be escaped or it changes the meaning of the URL.
The confusion is that the rules differ by position. A character safe in a path can be structural in a query.
| Character | In a path | In a query value |
|---|---|---|
| space | %20 | %20 or + |
| & | safe | must escape as %26 |
| = | safe | must escape as %3D |
| ? | must escape | safe after the first |
| # | must escape | must escape |
| / | structural | safe |
| + | safe | means space — escape as %2B |
That last row causes real bugs. In a query string + is historically a space, so a value containing a genuine plus — a phone number, a search for "C++" — arrives with it silently turned into a space unless escaped. This is why JavaScript has both encodeURI (for a whole URL) and encodeURIComponent (for one value). Use the second for anything going into a parameter.
A JWT is not opaque
A JSON Web Token is three Base64url segments separated by dots: a header, a payload, and a signature.
The header and payload are plain JSON, encoded and not encrypted. Anyone holding the token can read the payload — paste it into any decoder, including this one, and every claim inside is visible.
So a JWT payload must never contain anything you would not hand to the token holder. No passwords, no internal identifiers you rely on being secret, no personal data beyond what that user is entitled to see about themselves. This gets got wrong regularly, and the token is often sitting in browser storage where any script on the page can read it.
What the signature does and does not prove
The signature is a hash of the header and payload, produced with a secret or a private key. It proves the token was issued by whoever holds that key and has not been altered since.
It does not hide the contents, does not prove the token is still valid, and does not prove the bearer is the person it describes. Anyone who steals a valid token can use it, which is why expiry times matter.
- Always verify server-side. Decoding is not verifying. A client can decode a token and read the claims; only the server can check the signature against the key.
- Check
exp. An expired token is still perfectly decodable and must be rejected. - Reject
alg: none. The specification permits an unsigned token, and libraries that honour it accept anything. This has been a real vulnerability class. - Pin the algorithm. Do not let the token tell you how to verify it — that is how the RS256-to-HS256 confusion attack works.
Debugging safely
Decoding a token to see what is inside is a normal part of debugging, and where you do it matters.
A JWT from a production system is a live credential. Pasting one into an online decoder sends it to that service — and for however long it remains unexpired, whoever holds it can act as that user.
Use a decoder that runs in your browser rather than on a server. The JWT Decoder does the parsing locally, so the token never leaves the machine. Same argument applies to Base64 and URL encoding tools when the value being decoded is a key or a session identifier.
When to reach for which
| Problem | Use |
|---|---|
| Binary data in a text field | Base64 |
| A small image inline in CSS or HTML | Base64 data URI |
| A value inside a URL parameter | encodeURIComponent |
| Building a whole URL from parts | encodeURI |
| Proving a file downloaded intact | A hash, not encoding |
| Hiding a value from the user | None of these — encryption |
That last row is the whole point of the post. If the requirement is that someone cannot read a value, none of these tools does it, and reaching for Base64 because the output looks unreadable is how credentials end up exposed in plain sight.
Frequently asked questions
Is Base64 encryption?
No. It is a reversible transformation with no key — anyone can decode it instantly. If a sensitive value is Base64 encoded in a config file or a URL, treat it as fully exposed, exactly as you would plaintext.
Why does Base64 make files bigger?
Because it represents three bytes as four ASCII characters, an unavoidable 33% overhead. That is why email attachments inflate in transit and a 20 MB file can bounce off a 25 MB limit.
When should I use encodeURIComponent instead of encodeURI?
encodeURIComponent for a single value going into a parameter — it escapes the structural characters like & and = that would otherwise break the URL. encodeURI for a whole URL, where those characters are meant to stay structural.
Can anyone read a JWT?
Yes. The header and payload are Base64-encoded JSON, not encrypted, so anyone holding the token can decode and read every claim. Never put anything in a payload that the token holder should not see.
What does the JWT signature prove?
That the token was issued by whoever holds the signing key and has not been altered. It does not hide the contents, does not prove the token is unexpired, and does not prove the bearer is the person it describes.
Is it safe to paste a JWT into an online decoder?
Not a production one. A valid token is a live credential, and pasting it into a server-side decoder hands it over for as long as it remains unexpired. Use a decoder that parses in your browser.