Base64 vs URL Encoding
Both convert data into a safe-to-transmit format — but they solve completely different problems. Mixing them up is one of the most common sources of subtle encoding bugs in web development.
TL;DR
- Base64: converts binary data (images, files, keys) to printable ASCII. Output is 33% larger. Not URL-safe by default — use Base64URL for URLs.
- URL encoding (percent-encoding): makes arbitrary text safe in URLs by replacing special characters with %HH hex codes. Use
encodeURIComponent()for query params. - Base64 is not encryption — it's trivially reversible with no key. Never use it to hide sensitive data.
- Common mistake: embedding a Base64 string (containing +, /, =) directly in a URL without Base64URL conversion or percent-encoding.
At a Glance
| Attribute | Base64 | URL Encoding (Percent-Encoding) |
|---|---|---|
| Purpose | Encode binary data as printable ASCII text | Make arbitrary text safe inside URLs |
| Standard | RFC 4648 | RFC 3986 |
| Character set | A-Z, a-z, 0-9, +, /, = (64 + padding) | Unreserved chars only; others as %HH |
| Example output | SGVsbG8gV29ybGQ= | Hello%20World%21 |
| Output size | ~133% of input (33% overhead) | Varies — ASCII text often unchanged |
| URL-safe variant | Base64URL: + → -, / → _, drop = | This IS the URL-safe encoding |
| Reversible? | Yes — trivially, no key needed | Yes — trivially, use decodeURIComponent() |
| Encrypts data? | No — encoding only | No — encoding only |
| Use in query strings | Only with Base64URL or after percent-encoding | Designed for this — encode values only |
| Binary data support | Yes — primary use case | Limited — text focused |
| Common in | JWTs, data URIs, email attachments, PEM keys | URL query params, form submissions, path segments |
Encoding Examples
| Input | Base64 | URL Encoded |
|---|---|---|
| Hello World | SGVsbG8gV29ybGQ= | Hello%20World |
| user@example.com | dXNlckBleGFtcGxlLmNvbQ== | user%40example.com |
| price=100&tax=5 | cHJpY2U9MTAwJnRheD01 | price%3D100%26tax%3D5 |
| https://x.com/path | aHR0cHM6Ly94LmNvbS9wYXRo | https%3A%2F%2Fx.com%2Fpath |
| name: João | bmFtZTogSm/Do28= | name%3A%20Jo%C3%A3o |
| <script>alert(1)</script> | PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg== | %3Cscript%3Ealert(1)%3C%2Fscript%3E |
Quick Decision
Use Base64 when…
- Embedding images in HTML (data:image/png;base64,...)
- Encoding binary file attachments in email (MIME)
- Storing binary data in JSON or XML payloads
- Encoding cryptographic keys (PEM format)
- Transmitting binary data through text-only channels
- JWT tokens (uses Base64URL variant)
Use URL Encoding when…
- Embedding user input in URL query parameters
- Constructing URLs with special characters (spaces, &, =)
- Form submissions with application/x-www-form-urlencoded
- Encoding path segments in REST API URLs
- Building search query strings with arbitrary user text
- Encoding redirect URLs passed as query parameters
Real-World Patterns
The JWT URL Embedding Bug
A developer passes a JWT as a query parameter: /callback?token=eyJhbGc...+abc/xyz=. Standard Base64 uses + and /, which have special meaning in URLs. The + is decoded as a space; the / may be treated as a path separator. The server receives a corrupted token and authentication fails. The fix: JWTs use Base64URL (not standard Base64) — they replace + with -, / with _, and drop = padding. Always use a JWT library rather than manual Base64 encoding to avoid this class of bug.
Passing URLs as Query Parameters
A redirect parameter in a login flow: /login?redirect=https://app.com/dashboard?tab=settings&user=42. Without encoding, the outer URL parser sees & as a parameter separator and reads redirect=https://app.com/dashboard?tab=settings and then user=42 as a separate parameter. The correct construction: encodeURIComponent('https://app.com/dashboard?tab=settings&user=42') → 'https%3A%2F%2Fapp.com%2Fdashboard%3Ftab%3Dsettings%26user%3D42'. Then the outer URL sees it as a single opaque value.
Data URIs with Base64
An inline SVG or image in HTML: <img src="data:image/png;base64,iVBORw0KGgo...">. The browser decodes the Base64 string and renders the image without any HTTP request — useful for small icons in components or email HTML where external URL references may be blocked. The overhead: Base64 increases file size by 33%, and inline data URIs aren't cached separately from the HTML. Best for small images (under 2–5KB); for larger images, external URLs with proper caching are more efficient.
API Basic Authentication Header
HTTP Basic Authentication sends credentials as: Authorization: Basic Base64(username:password). The colon-separated username:password string is Base64-encoded and sent in every request header. This is not secure without HTTPS — Base64 is trivially decoded by anyone intercepting the traffic. With HTTPS, it's acceptable for simple server-to-server API auth. The encoding in Python: import base64; base64.b64encode(b'user:pass').decode(). The common mistake: thinking the Base64 makes the credentials secure — it does not. Always use HTTPS.
Verdict: Different Tools for Different Problems
Base64 and URL encoding solve completely different problems. Base64 makes binary data transmittable as text. URL encoding makes arbitrary text safe inside URLs. Using Base64 where you need URL encoding (embedding raw Base64 in a URL) breaks URLs. Using URL encoding where you need Base64 (encoding binary data as %XX sequences) is inefficient and wrong.
Remember: if you're putting data in a URL query string, use encodeURIComponent(). If you're encoding binary data for a text channel, use Base64. If you're doing both (putting Base64 in a URL), use Base64URL — the variant designed specifically for URL contexts.
Decision Checklist
| Scenario | Use |
|---|---|
| Embedding image in HTML without external request | Base64 |
| User input in URL query parameter | URL Encoding |
| Sending JWT in URL or query string | Base64URL |
| HTTP Basic Auth header credentials | Base64 |
| Encoding redirect URL as a query param | URL Encoding |
| PEM-encoded certificate or private key | Base64 |
| Constructing a search query with special chars | URL Encoding |
| Email attachment (MIME multipart) | Base64 |
| Path segment with slashes or special chars | URL Encoding |
| Binary file in JSON payload | Base64 |
| OAuth2 state or code_verifier in URL | URL Encoding (or Base64URL) |
| Hiding sensitive data from casual inspection | Neither — use encryption |
Frequently Asked Questions
Why is Base64 not URL-safe?
Standard Base64 uses three characters that have special meaning in URLs: + (represents a space in query strings), / (path separator), and = (key=value separator). If you put a standard Base64 string in a URL query parameter, + will be decoded as a space, / may be interpreted as a path separator, and = may confuse parameter parsers. The solution is Base64URL (RFC 4648 §5): replace + with -, replace / with _, and omit padding =. JWT tokens use Base64URL for exactly this reason — they are frequently embedded in URLs and Authorization headers.
What is percent-encoding (URL encoding)?
Percent-encoding (RFC 3986) replaces characters that have special meaning in URLs with a % followed by the character's hexadecimal ASCII code. A space becomes %20, & becomes %26, = becomes %3D, + becomes %2B. Only unreserved characters (A-Z, a-z, 0-9, -, _, ., ~) can appear in URLs unencoded. Everything else must be percent-encoded. Most languages provide built-in functions: JavaScript encodeURIComponent(), Python urllib.parse.quote(), Java URLEncoder.encode().
What does Base64 actually do?
Base64 converts arbitrary binary data into a string using only 64 printable ASCII characters (A-Z, a-z, 0-9, +, /). It groups input bytes in sets of 3 (24 bits) and converts each group into 4 Base64 characters (6 bits each). This produces output 33% larger than the input — 3 bytes become 4 characters. It is used primarily to transmit binary data over text-based channels: embedding images in HTML (data URIs), sending files in email (MIME), storing binary data in JSON payloads, and encoding cryptographic keys and certificates in PEM format.
Is Base64 a form of encryption?
No — Base64 is encoding, not encryption. It is trivially reversible by anyone without any key or secret. Decoding a Base64 string requires no authentication, no key, no secret. Base64 provides zero confidentiality. It is purely a representation format — a way to represent binary data as ASCII text. Never use Base64 to 'hide' sensitive data. This is a common beginner mistake: hardcoding an API key as Base64 in client-side JavaScript provides no security — any user who inspects network requests or the source code can instantly decode it.
When should I use encodeURI vs encodeURIComponent in JavaScript?
encodeURIComponent() encodes everything except unreserved characters (A-Z, a-z, 0-9, -, _, ., ~, !, *, ', (, )). Use it for encoding individual query parameter values or path segments. encodeURI() does not encode characters that are valid in a complete URI (/, ?, #, &, =, :, @, etc.) — it encodes a full URL without breaking its structure. The correct usage: build the full URL with encodeURIComponent() for each parameter value, then treat the assembled URL as a unit. Never use encodeURI() for query parameter values — it will leave = and & unencoded, breaking the query string.
What is the difference between application/x-www-form-urlencoded and percent-encoding?
application/x-www-form-urlencoded is a specific encoding for HTML form submissions that uses percent-encoding with one important difference: spaces are encoded as + rather than %20. This is a legacy convention from early web forms. Pure RFC 3986 percent-encoding always uses %20 for spaces. Confusing the two causes bugs: if your backend expects form-urlencoded (spaces as +) but receives %20 from a non-form client (or vice versa), space characters will be mishandled. Always use %20 in URL query strings; + is only unambiguous in form-urlencoded bodies.
Can Base64 encode any data? What about Unicode?
Base64 encodes bytes — arbitrary binary data — not text directly. For Unicode text, you must first encode the text to bytes (usually UTF-8), then Base64-encode the bytes. btoa() in browsers only handles Latin-1 characters and will throw on multi-byte Unicode. The correct approach: new TextEncoder().encode(string) → Uint8Array → Base64. atob() has the same limitation on decode. Many developers hit this bug when trying to Base64-encode strings containing emoji, Chinese characters, or diacritics. Always ensure UTF-8 encoding before Base64.
What is double URL encoding and when is it a problem?
Double URL encoding happens when a percent-encoded string is encoded again: a space → %20 → %2520 (% itself encoded as %25). This occurs when developers accidentally encode an already-encoded value, or when frameworks auto-encode values that were manually encoded. It is also a known security issue: some web application firewalls and input validators check for %20 (space) as a SQL injection or path traversal pattern, but miss %2520 (double-encoded space). Always decode exactly once at the appropriate layer. If you receive a URL from another system, check whether it's already encoded before re-encoding.
Related Comparisons
Verdict: Choose Based On Your Situation
Base64
- You need to transmit binary data as text
- You're embedding data in JSON, XML, or HTML attributes
- You need reversible encoding of any data type
- You're using data URIs for inline images
URL Encoding
- You're encoding query parameters or form data
- You need compact encoding of text with special characters
- You're building API request URLs
- You want human-readable output