Skip to content

Base64 Encode / Decode

Encode and decode Base64 instantly in your browser. Includes automatic size-overhead calculation — handy for data URIs and API testing.

100% Free No signup Browser-only 5 languages Dark mode

If something is broken or not displaying correctly, let us know via the contact form — we use the feedback to improve.

Other Tools

Related articles

📖 Where people get stuck

Converts text or files to Base64 and back, with URL-safe output and the legacy MIME line break every 76 characters. Everything runs in the browser. Base64 is an encoding that represents bytes using only 64 ASCII characters — it is neither encryption nor compression. Anyone can reverse it in a second, and the data always grows by about 33 percent. Practically every incident involving Base64 starts from confusing one of those two points.

Case What happens What to do
Non-ASCII text garbles, or throws an exception Base64 operates on bytes, not characters. The step that turns characters into bytes — choosing the character encoding — happens outside Base64, and when sender and receiver disagree about it the text comes out mangled. In the browser, btoa() accepts only the Latin-1 range, so handing it Japanese throws InvalidCharacterError. PHP base64_encode(), by contrast, encodes whatever bytes it is given, so if the source was Shift-JIS then Shift-JIS is what goes in — and being silent makes that the more dangerous case. Convert to UTF-8 bytes before encoding. In JavaScript, produce a Uint8Array with new TextEncoder().encode(str), encode that, and decode with new TextDecoder().decode(bytes) on the way back. As long as that round trip is symmetric, nothing garbles between any pair of languages. When the data crosses a system boundary, state in the API documentation that the Base64 payload is UTF-8: the encoding cannot be recovered from the Base64 string itself, so if you do not say it, the other side is guessing.
It breaks when placed in a URL or query string The +, / and = characters of standard Base64 all mean something else inside a URL. The dangerous one is +, which a query string reads as a space, so the value is corrupted on decode. What makes it nasty is that the failure looks probabilistic: whether the output contains a + depends on the input, so it presents as passing every test and then failing on one particular record in production. / collides with the path separator and = with parameter assignment. Use the URL-safe form when it goes into a URL: + becomes -, / becomes _, and the trailing = is dropped (base64url in RFC 4648). JWT is standardised on exactly this, so building a JWT by hand leaves you no choice. When in doubt, pick URL-safe from the start rather than percent-encoding a standard Base64 string — double encoding tends to end with only one layer undone and a stray %2B sitting in your data. Dropping the = padding is safe, since the length determines it.
Inlining as a data URI made things slower You do remove a request, but the data grows by 33 percent and the browser cache no longer applies. Inline an image into CSS and the stylesheet itself balloons, and stylesheets block rendering, so first paint gets slower. Twenty images of 10KB each turn what would have been twenty parallel requests into one 266KB render-blocking resource. Worse, changing a single image invalidates the cache for the entire stylesheet. Since HTTP/2 the cost of an extra request has fallen sharply, so the premise that requests are what you should be minimising is itself out of date. Keep data URIs to small icons of a few kilobytes. Above that, load it as a normal <img> and take the caching and responsive-image (srcset) wins instead — it comes out faster. Do not Base64 an SVG: SVG is text, so percent-encoding it with encodeURIComponent is smaller than Base64 and still compresses under gzip. The decision rule is simple — compare how often the image changes with how often the stylesheet is served. Only assets like a logo, rarely changed and used on every page, are good candidates for inlining.

Base64 is not even obfuscation. Writing password: cGFzc3dvcmQ= in a config file makes it unreadable at a glance and nothing more; it is not a secret. Kubernetes Secrets use exactly this form, and that is encoding, not encryption — anyone who can see the output of kubectl get secret -o yaml can decode it, which is why external secret managers and encryption at rest are layered on top. GitHub secret scanning and similar leak detectors decode Base64 and look inside, so committing one is detected in the ordinary way. Put the other way round: any place where someone decided it was fine because it was Base64 is almost certainly a vulnerability. One more thing — watch how line wrapping is handled. Email and PEM formats break the text every 64 to 76 characters, and some implementations error out if you decode without stripping those newlines, while some older systems refuse input that arrives as a single long line.

📖 How to Use

  1. 1
    Paste input
    Paste raw text to encode or Base64 string to decode.
  2. 2
    Toggle direction
    Click Encode/Decode to switch. Result is instant and size overhead is shown.
  3. 3
    Pick URL-safe variant
    For URLs or JWTs, pick URL-safe (no =, + → -, / → _).

❓ Frequently Asked Questions

Why does Base64 grow size by 33%?
3 bytes (24 bits) become 4 chars of 6 bits each — that is 4/3 ≈ 133% size. See our blog post.
Can I encode binary files?
Yes. Drag & drop a file to read as binary and encode. Usable as data: URL directly.
Is my data sent to the server?
No. Encoding/decoding happens entirely in your browser via FileReader / TextEncoder.
How are newlines handled?
Input newlines are preserved. An option to break output every 76 chars (legacy MIME) is available.
🐛 Found a bug or issue with this tool?

Free to use, no signup. Even just the steps to reproduce are helpful. Reports go directly to the operator and help us fix issues.

* Browser info (UA / screen / language / URL) is sent automatically to help reproduce the issue