Base64 Encoding: When You Should and Shouldn't Use It (2026 Guide)

#1Base64 encoding: when you should and should not use it
What we tested: We evaluated the tools and techniques described in this article using real developer workflows. All browser-based tools on this site run locally, your data never leaves your machine.
Base64 is useful when bytes need to survive a text-only boundary.
It is not encryption, and it is usually the wrong answer when people use it to hide data, move large files around casually, or avoid fixing a transport problem.
#2What base64 actually is
Base64 takes 3 input bytes (24 bits), splits them into four 6-bit groups, and maps each 6-bit value to a printable character from a 64-character alphabet. Three bytes in, four characters out. Padding (=) is added when the input length is not a multiple of 3, so the output is always a multiple of 4 characters.
Input: M a n (3 bytes)
Bytes: 77 97 110 (decimal)
Bits: 01001101 01100001 01101110
Regroup: 010011 010110 000101 101110
Index: 19 22 5 46
Output: T W F uMan → TWFu. Three bytes, four characters. If the input were Ma (2 bytes), the output would be TWE=, one = pad. If M (1 byte), TQ==, two pads. The pad characters are not data; they exist so the encoded length is always divisible by 4, which makes streaming decoders simpler.
The standard alphabet (RFC 4648 §4) uses A-Z, a-z, 0-9, +, /. The URL-safe alphabet (RFC 4648 §5) replaces + with - and / with _ so the encoded string can travel in URL paths and query strings without further escaping. Both are still 64 characters; they are not different encodings, just different lookup tables.
#2The 33% size cost
This is the single most-cited number in base64 discussions, and it matters more than people think:
- A 100 KB image becomes a 133 KB base64 string.
- A 1 MB PDF becomes a 1.33 MB base64 string.
- An 8 KB JWT payload (already absurdly large) is 11 KB on the wire.
The cost is in three places:
- Bandwidth, 33% more bytes flow over every hop.
- Memory, both the encoded and decoded forms typically exist in memory at the same time during a round-trip.
- CPU, base64 encoding and decoding are fast, but at scale they show up in flamegraphs. Inline a 2 MB image as base64 in every page load and you are doing meaningful work on every cold start.
That 33% is a hard floor. Any base64-over-the-wire design needs to be okay with paying it.
#2When you should use base64
#31. Binary in JSON or other text-only payloads
JSON has no native binary type. If you need to send raw bytes, a small file, a cryptographic signature, a hashed identifier, base64 is the standard escape hatch. Every JSON parser everywhere handles it; you control the encoding on both sides.
{
"filename": "receipt.pdf",
"content_type": "application/pdf",
"data": "JVBERi0xLjQKJeLjz9MKMyAwIG..."
}This is the right call for small binary payloads, a thumbnail, a signature, a short blob. For multi-megabyte files, use multipart upload or a presigned URL instead; the 33% overhead and the loss of streaming both hurt.
#32. JWT payloads and headers
The "Base64" in JWT is actually base64url, the URL-safe variant, with padding stripped. The format is <header>.<payload>.<signature>, each part being base64url. The goal is not secrecy (JWTs are readable once decoded, see JWT Tokens Explained); it is to keep the token safe for URLs, logs, and pasteable text transport. Decode any JWT in your browser at the Base64 encoder (or the dedicated JWT Decoder); they show the exact same payload bytes.
#33. Authorization: Basic HTTP header
The HTTP Basic auth scheme is Authorization: Basic <base64("username:password")>. The encoding is a transport detail, not a security measure, every interceptor on the network reads the credentials trivially. Basic auth is acceptable only over HTTPS, and even then is being replaced by bearer tokens for almost every use case. But when you do need it (legacy clients, internal-only services, simple CI bots), base64 is the only correct encoding.
#34. Tiny data: URIs
A data:image/svg+xml;base64,... URI inlines content directly into HTML, CSS, or a JS string. Useful for:
- Sub-1 KB SVG icons that would otherwise be a separate HTTP request. The base64 overhead is dwarfed by the per-request cost of an HTTP round-trip.
- Email-safe images where the rendering client cannot reach external URLs.
- Inline previews in tools (file pickers, image editors) that render before upload.
For SVG specifically, prefer URL encoding (data:image/svg+xml;utf8,<svg...) over base64, it is usually smaller because SVG is already text. For binary images, base64 is the only option.
#35. Email MIME attachments
SMTP is a 7-bit transport. MIME attachments use Content-Transfer-Encoding: base64 to ship binary through it. Every email client you have ever used decodes this on receipt. The line-wrapping rule (76 characters per line, CRLF) is part of the MIME spec and is the source of bug #3 below.
#36. Cryptographic blobs
Hashes, signatures, public keys, certificates, derived secrets, anything that is byte-shaped but needs to live in a config file, environment variable, or transport that expects text. Base64 (or hex, for shorter blobs) is the universal interchange format.
#2When you should not use base64
#31. As "security"
This bears repeating because it shows up in code reviews monthly: base64 is not encryption, not obfuscation, not security in any meaningful sense. Any human can decode a base64 string at sight (TWFu is just Man), and any tool can decode any base64 string in milliseconds. Storing a password as base64, sending an API key as base64, or "hiding" a token in a base64 payload provides exactly zero protection. Use HTTPS for transport confidentiality and proper encryption (AES-GCM, libsodium, KMS) for at-rest confidentiality.
#32. Storage of files you already have on disk
Storing a file as a base64 string in your database is almost always wrong. You pay:
- 33% more storage forever.
- Every read decodes and re-encodes, burning CPU.
- You cannot stream, the entire blob must materialize before processing.
- Database indices and full-text search are useless on the blob.
Store the file in object storage (S3, R2, GCS) and put the URL or key in the database. The base64-in-Postgres pattern is a code smell that always means "I did not want to set up S3 yet."
#33. Embedding large images in HTML or CSS
A 500 KB hero image base64-inlined into CSS makes that CSS file 665 KB, and that CSS is render-blocking. Every page load downloads the image, even on pages that do not display it. Caching breaks too, the inlined version cannot be cached separately from the CSS, so a single byte change in any unrelated CSS rule invalidates the entire image as well. Use a regular <img> tag or background-image: url(...) and let the browser cache the asset properly.
The break-even is roughly 2 KB: below that, the saved HTTP request often wins; above that, the cost of bloated, render-blocking, uncacheable CSS dominates.
#34. As a "binary format" for high-throughput systems
If you are designing a service-to-service protocol where every byte counts, gRPC, Protocol Buffers, MessagePack, CBOR, do not wrap them in JSON and base64 the binary fields. Use a transport that handles binary natively. JSON+base64 is appropriate when you are crossing a JSON-only boundary (browser fetch, third-party webhook, public REST API); it is wrong when you control both sides and could just speak binary.
#35. As a primary key or stable identifier
Base64 strings are case-sensitive (A ≠ a), contain + and / (URL-unsafe) or - and _ (URL-safe), and may or may not have padding. Subtle representational variants of the same bytes can produce different strings. UUIDs, ULIDs, KSUIDs are designed for this; base64 is not.
#2Standard vs URL-safe vs Unpadded, RFC 4648 Variants
Standard (RFC 4648 §4): A-Z a-z 0-9 + / = JSON, MIME, Basic auth
URL-safe (RFC 4648 §5): A-Z a-z 0-9 - _ = URLs, filenames
Unpadded: either of the above, with trailing `=` strippedJWT uses URL-safe and unpadded, the = characters are stripped, and the decoder must re-pad before decoding. AWS Cognito tokens, OIDC ID tokens, OAuth 2.0 PKCE code_verifier all use the same convention.
When you see a base64-looking string, check three things before processing:
- Alphabet, does it contain
+and/, or-and_? Standard or URL-safe. - Length, is it a multiple of 4? If not, padding has been stripped and you need to add
=characters yourself. - Whitespace, MIME base64 has CRLF every 76 characters. Strip it before decoding, or the decoder will choke.
The base64 encoder/decoder handles all three variants and tells you which one you pasted.
#2The Four Production Base64 Bugs
#31. Padding mismatch ("Invalid base64 string")
Symptom: the encoder strips padding, the decoder requires it (or vice versa), so decoding fails on otherwise valid input. JWT libraries handle this transparently; generic base64 decoders do not.
Fix: before decoding, pad the input length up to a multiple of 4 with =:
function padBase64(s) {
const pad = (4 - (s.length % 4)) % 4;
return s + "=".repeat(pad);
}Or use the language-native unpadded decoder if your runtime has one, Python's base64.urlsafe_b64decode requires padding; base64.b64decode with validate=False is lenient. Node's Buffer.from(s, "base64") is fully lenient and rarely the source of this bug, but its strictness has tightened in recent versions, pin your assumptions.
#32. URL-safe vs standard confusion
Symptom: a base64 string copied from a URL contains - and _, you pass it to a standard decoder, the decoder either rejects it or silently produces wrong bytes. Or the reverse, a standard base64 string with + ends up in a URL, the + becomes a space when URL-decoded, the receiver decodes garbage.
Fix: at every system boundary, document which variant is in use, and use the matching decoder. If you have to be lenient on input, replace - → + and _ → / before decoding as standard. Never put a standard-alphabet base64 string into a URL without first URL-encoding it (%2B, %2F), and at that point, just use URL-safe base64 to begin with.
#33. MIME line-wrapping breaking non-MIME decoders
Symptom: a base64 string copied out of an email source view has newlines every 76 characters. You paste it into a decoder that does not strip whitespace. Decoder fails or produces wrong output.
Fix: strip whitespace before decoding. In Node:
Buffer.from(s.replace(/\s+/g, ""), "base64");In Python:
import base64
base64.b64decode("".join(s.split()))Most language standard libraries do this automatically; some web-platform APIs do not. When in doubt, strip first.
#34. Treating base64 as a hash or fingerprint
Symptom: developer base64-encodes a value to "anonymize" it before logging or sending to analytics, believing it cannot be reversed. It can. The same input always produces the same base64 output (it is a function, not a hash), and the output is trivially reversible.
Fix: if you want a non-reversible representation, use SHA-256 (and even then, low-entropy inputs are recoverable by dictionary attack, see the discussion in the Stripe Webhook Testing guide for an analogous HMAC-vs-hash distinction). If you want confidentiality, use authenticated encryption. Base64 is not the answer to either question.
#2A Quick Reference Code Snippet (Encode/Decode in 5 Languages)
// JavaScript (browser)
const encoded = btoa("hello"); // "aGVsbG8="
const decoded = atob("aGVsbG8="); // "hello"
// btoa/atob only handle Latin-1; for full Unicode:
const encoded2 = btoa(unescape(encodeURIComponent("héllo")));
// Node.js
Buffer.from("hello").toString("base64"); // "aGVsbG8="
Buffer.from("aGVsbG8=", "base64").toString(); // "hello"
Buffer.from("hello").toString("base64url"); // URL-safe, unpadded
// Python
import base64
base64.b64encode(b"hello").decode() // "aGVsbG8="
base64.b64decode("aGVsbG8=").decode() // "hello"
base64.urlsafe_b64encode(b"hello").rstrip(b"=") // URL-safe, unpadded
// Go
import "encoding/base64"
base64.StdEncoding.EncodeToString([]byte("hello"))
base64.StdEncoding.DecodeString("aGVsbG8=")
base64.RawURLEncoding.EncodeToString(...) // URL-safe, unpadded
// Bash
echo -n "hello" | base64 // "aGVsbG8="
echo "aGVsbG8=" | base64 -d // "hello"The -n flag on echo matters, without it, the trailing newline is encoded into the base64, and your output ends in Cg== instead of clean bytes. Same trap as the Stripe webhook signing flow (see Stripe Webhook Testing Without Stripe CLI).
#2Frequently Asked Questions
#3When should I use base64 encoding?
Use base64 when you need to move binary data through a transport that only handles text. The clear wins are: binary fields inside JSON payloads, JWT headers and payloads, Authorization: Basic credentials, MIME email attachments, and tiny (sub-2 KB) data: URIs for icons. The clear losses are storage (database columns, files on disk), large image embedding (render-blocking and uncacheable), and any context where you control both ends and could use a binary transport directly. The 33% size overhead is a hard cost, every time you encode 3 bytes, you produce 4 characters. If you can avoid that cost by switching transports, do.
#3Is base64 secure?
No. Base64 is an encoding, a one-to-one mapping between bytes and printable characters. Anyone with the algorithm (which is public, RFC 4648) can reverse it in microseconds. Storing a password as base64 is the same as storing it in plaintext. Sending an API key "base64-encoded" provides zero protection on the network. Use HTTPS for transport security and a proper KMS or libsodium-style library for at-rest encryption. If you see "obfuscated" or "encrypted" in a code comment next to a base64 call, that comment is wrong and the code is a vulnerability waiting to be reported.
#3What is the difference between base64 and base64url?
Same algorithm, different alphabet. Standard base64 (RFC 4648 §4) uses + and / as the 63rd and 64th characters; URL-safe base64 (§5) uses - and _. The point is that + and / have special meaning in URLs (+ becomes a space when URL-decoded, / is a path separator), so a standard-encoded base64 string cannot ride safely in a URL without further escaping. JWTs use base64url, with the trailing = padding also stripped. Convert between them by character substitution: standard → URL-safe is + → -, / → _; the reverse for decoding. Both produce the same bytes; the alphabet swap is purely cosmetic.
#3How much does base64 increase file size?
Exactly 33.3% on average, every 3 input bytes become 4 output characters, plus 0–2 = padding characters at the end. A 1 KB file becomes 1.33 KB. A 100 KB file becomes 133 KB. The cost is fixed and unavoidable; it is the mathematical price of squeezing 256 possible byte values into 64 possible character values. If you also wrap lines (76 characters per line for MIME), add another ~3% for the CRLF separators. The number to memorize: multiply by 4/3 to get the encoded length, or divide by 3/4 to recover the decoded length.
#3Can I base64-encode an image to embed in HTML?
You can, but you usually shouldn't for anything over 2 KB. The browser cannot cache an inlined image separately from the HTML or CSS containing it, so every page load re-downloads it. The image cannot be lazy-loaded, cannot be served at a different resolution per device, and bloats the document so much that initial render is delayed. Below 2 KB, small SVG icons, transparent placeholders, the like, the saved HTTP request usually wins; above 2 KB, regular <img> or background-image: url(...) with a properly cached asset is always better. For SVG specifically, prefer data:image/svg+xml;utf8,<svg...> (URL-encoded) over base64, SVG is already text, so base64 only adds overhead.
Base64 is a 50-year-old encoding that does exactly one thing well, let binary ride through text-only transports. It is not encryption, it is not compression, it is not magic. Used at the right boundary (JSON, JWT, MIME, Basic auth, tiny data URIs) it is invisible and correct. Used at the wrong boundary (storage, large image embedding, "obfuscation") it is a performance and security smell that future-you will have to rip out.
Encode, decode, and inspect any base64 string, standard or URL-safe, padded or not, with or without MIME line wrapping, at the AllDevToolsHub Base64 Encoder. Runs entirely in your browser; nothing is transmitted. For the surrounding encoding and transport surface, see the JWT Tokens Explained guide, the Webhook Debugging Playbook, and the HTTP Headers Reference.
Written by Rahul Jalavadiya, founder of AllDevToolsHub. All tools run locally in your browser.
#2Sources / Further reading
- IETF - RFC 4648: The Base16, Base32, and Base64 Data Encodings
- MDN Web Docs - Base64 encoding in JavaScript
- IETF - RFC 7515: JSON Web Signature (Base64url in JWTs)
#2Try These Tools
Quick Summary
>- A practical guide to base64 in 2026 — when to use it (binary in text-only transports, data URIs for tiny assets, JWT payloads, Authorization headers), when to absolutely not (storage, large image embedding, anything you think it makes "secure"), the 33% size cost, the URL-safe and unpadded variants, and the four common base64 bugs that catch teams in production.
Key Takeaways
- Base64 converts binary data to ASCII text using a 64-character alphabet — it increases size by ~33% and provides zero security.
- Use Base64 for text-only transport (JSON payloads, data URIs, email attachments) — never for storage optimization or obfuscation.
- URL-safe Base64 (RFC 4648 §5) replaces + and / with - and _ to avoid URL encoding issues.
When to use it
- Embedding small images directly in CSS or HTML as data URIs to reduce HTTP requests.
- Encoding binary data (images, files) in JSON API payloads that only support text.
- Encoding JWT header and payload segments per RFC 7515.
Common Mistakes
- Treating Base64 as encryption — anyone can decode it. Use AES or ChaCha20 for confidentiality.
- Using Base64 to reduce file size — it increases size by 33%. Use compression (gzip, Brotli) instead.
- Forgetting to strip padding (=) in URL-safe contexts where = has special meaning.
Base64 Encoding: When You Should and Shouldn't Use It (2026 Guide), Frequently Asked
Is Base64 encoding secure?
No. Base64 is encoding, not encryption. It converts binary to text for transport but provides zero confidentiality. Anyone who sees the Base64 string can decode it.
Why does Base64 make files larger?
Base64 maps every 3 bytes of input to 4 ASCII characters. This 33% overhead is the cost of representing arbitrary binary data using only printable ASCII characters.
Tools Mentioned in This Article
Tools, tactics, and toughened-up tips, once a week
New tools, deep-dives on developer workflows, and the occasional gem we found this week. No spam, no tracking. Unsubscribe anytime.
Found an error or have feedback?
We correct errors quickly and document changes in our changelog. Report issues at support@alldevtoolshub.com.