Base64, URL Encoding and JWT Explained
These three turn up whenever data has to travel as text: in URLs, HTTP headers, JSON and login tokens. None of them hides anything. This guide explains how each one works, where you meet it, and the mistakes behind most problems.
Encoding Is Not Encryption
Encoding changes how data is written so that it survives a channel that only accepts certain characters. Anyone can reverse it, because no key is involved. Encryption also changes the data, but only someone with the key can turn it back. A hash is different again: it is a one-way fingerprint that cannot be turned back at all, as explained in Hash Functions Explained.
Base64 and URL encoding are encodings. A JWT is usually signed, which proves who issued it and that it was not changed, but its contents are only encoded. If a value must stay secret, it needs encryption, not one of these formats.
How Base64 Works
Base64 reads the data three bytes (24 bits) at a time and splits them into four groups of six bits. Each group is a number from 0 to 63 and is written as one character of a 64-character alphabet: A–Z for 0–25, a–z for 26–51, 0–9 for 52–61, then + and /. The word "Man" shows the steps:
| Step | M | a | n |
|---|---|---|---|
| Byte values | 77 | 97 | 110 |
| Bits | 01001101 | 01100001 | 01101110 |
| Four 6-bit groups | 010011 · 010110 · 000101 · 101110 = 19 · 22 · 5 · 46 | ||
| Base64 | T W F u → TWFu | ||
When the input length is not a multiple of three, the last group is filled up and marked with padding: one leftover byte gives two characters and ==, two leftover bytes give three characters and =. "Ma" becomes TWE= and "M" becomes TQ==.
Because every three bytes become four characters, Base64 makes data about a third larger, plus line breaks where a format adds them. A 3 MB photo sent as an email attachment travels as roughly 4 MB of text, and an image embedded in a stylesheet as a data URL is about a third heavier than the file.
Where You Meet Base64
- Email attachments (MIME) are Base64 text, wrapped at 76 characters per line.
- Data URLs such as
data:image/png;base64,iVBORw0…put a small file straight into HTML or CSS. - HTTP Basic authentication sends
user:passwordas Base64 in the Authorization header. That only hides the password from casual eyes, so it is safe only over HTTPS. - Certificates and keys in PEM files are Base64 between
-----BEGIN …-----and-----END …-----lines, wrapped at 64 characters. - JSON and XML carry binary values, such as a signature or a thumbnail, as Base64 strings because they have no binary type.
Base64URL and Padding
The + and / of standard Base64 have meanings in URLs and file paths, so a second alphabet, Base64URL, uses - and _ instead. It usually leaves out the = padding as well, since the decoder can work out the length. JWTs, many API tokens and some file names use Base64URL. To turn standard Base64 into Base64URL, replace + with -, / with _ and drop trailing = signs; to go back, reverse the replacements and add = until the length is a multiple of four.
Percent-Encoding in URLs
A URL may contain only a limited set of characters, and some of them structure the address: / and
? separate the path and query, & and = separate parameters, # starts the fragment. When a value
contains one of these characters, or anything outside ASCII, it is percent-encoded: each byte of
its UTF-8 form is written as % followed by two hexadecimal digits. "café" becomes
caf%C3%A9, because é is the two bytes C3 A9.
The letters, digits and - . _ ~ never need encoding. These characters most often cause trouble:
| Character | Encoded | Why it matters |
|---|---|---|
| space | %20 | Not allowed in a URL |
| & | %26 | Would start a new parameter |
| = | %3D | Would end the parameter name |
| ? | %3F | Would start the query |
| # | %23 | Would start the fragment, which is not sent to the server |
| / | %2F | Would add a path segment |
| + | %2B | Read as a space in form data |
| % | %25 | Would start an escape sequence |
Encode each value on its own and then assemble the URL. In JavaScript,
encodeURIComponent does this for one value and encodes / ? & = # too, while
encodeURI is meant for a whole address and leaves those characters alone. Using
encodeURI on a single value is a common source of broken query strings.
Plus Signs, Spaces and Double Encoding
HTML forms send their fields as application/x-www-form-urlencoded, where a space
becomes +. In the general URL syntax, + is just a plus sign. So a search for "C++" sent as
q=C++ arrives as "C" followed by two spaces on a server that decodes form data; it
should be sent as
q=C%2B%2B. When you decode a query string by hand, find out which rule produced it.
Double encoding happens when an already encoded value is encoded again: %20 turns into %2520, because the % itself becomes %25. If you see %25 followed by two hexadecimal digits in a URL, something in the chain encoded twice; decode once and check where the extra step happens.
What Is Inside a JWT
A JSON Web Token (JWT) carries claims, such as who the user is and until when the token is valid, from the system that issues it to the systems that accept it. The common signed form has three Base64URL parts separated by dots: a header, a payload and a signature. Decoded, the first two are plain JSON:
Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"sub": "user-42", "role": "editor", "iat": 1767225600, "exp": 1767229200}
The standard (RFC 7519) defines a few registered claims; applications add their own, like
role above.
| Claim | Meaning |
|---|---|
| iss | Issuer: who created the token |
| sub | Subject: usually the user or account ID |
| aud | Audience: the service the token is meant for |
| exp, nbf, iat | Expires, not before, issued at: Unix time in seconds |
| jti | Token ID, used to detect reuse or to revoke one token |
Because the payload is only encoded, anyone who holds the token can read it. Keep personal data and secrets out of it, and keep tokens small: they travel with every request, and many servers limit the size of request headers.
Signed Is Not Secret: How a JWT Is Checked
The signature is calculated over the header and payload. With HS256, the issuer and the receiver share one secret and use HMAC with SHA-256. With RS256 or ES256, the issuer signs with a private key and receivers check with the matching public key, so they cannot create tokens themselves.
A receiving server has to verify the signature with the expected algorithm and key, and check exp, nbf, iss and aud, before it trusts any claim. Decoding, as the LeedyBox tool does, skips all of that: it shows what a token says, not whether it is genuine. Libraries must also reject the algorithm "none", which marks an unsigned token, unless unsigned tokens are expected. When the contents themselves must stay hidden, the token is encrypted instead (JWE), and it then has five parts that cannot be read without the key.
Common Problems and Fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Base64 decodes to garbled text | The data is binary, or text in another encoding | Check what was encoded; save binary data as a file instead |
| "Invalid character" when decoding | A data: prefix, quotes or the Base64URL alphabet | Remove everything before the comma; use a decoder that accepts - and _ |
| A parameter arrives cut off | An unencoded & or # in the value | Encode each value with encodeURIComponent |
| %2520 in a URL | The value was encoded twice | Encode once, at the point where the URL is built |
| Token rejected although exp is in the future | Wrong audience or issuer, a clock difference, or a key mismatch | Compare aud, iss and the signing key with what the server expects |
| Expiry date thousands of years ahead | exp written in milliseconds instead of seconds | Store exp, nbf and iat as seconds |
Frequently Asked Questions
Is Base64 a way to protect a password or API key?
No. Base64 can be reversed by anyone in a second. A credential encoded in Base64, for example in a configuration file or a URL, is as exposed as plain text.
Can I put personal data in a JWT payload?
Avoid it. The payload can be read by anyone who has the token, including browser extensions, logs and proxies that see the request. Put an ID in the token and look up the details on the server.
Does URL encoding protect against injection attacks?
Only in the sense that a value cannot break the structure of the URL. The server still has to validate and escape the decoded value for wherever it uses it, such as SQL, HTML or a shell command.
Why is my Base64 string longer than the file?
Every three bytes become four characters, so the text is about 33% larger, and line breaks add a little more. For large files, sending the bytes directly is more efficient than Base64.