Skip to content
Developer 8 min · Feb 22, 2025

How Base64 Encoding Works and When to Use It

The mechanics of Base64 per RFC 4648, common use cases, and when you should NOT use it.

H
HT99 Tools Editorial Team
Editorial Team

What Base64 Is, per RFC 4648

Base64 is a binary-to-text encoding scheme formally specified in RFC 4648 (Josefsson, October 2006), which is also designated Internet STD 35. Its purpose is narrow and specific: represent arbitrary binary data using only 64 printable ASCII characters, so that the data can pass through systems designed for text. Base64 is an encoding, not an encryption and not a compression — the encoded output is decodable by anyone who knows the scheme, and it is roughly 33 percent larger than the input.

The 64 characters in the standard Base64 alphabet are A-Z, a-z, 0-9, +, and /, in that order. A 65th character, =, is used for padding at the end of the encoded output. Each Base64 character carries 6 bits of information (because 2^6 = 64), so three input bytes (24 bits) become four output characters (also 24 bits). This 3-to-4 ratio is where the 33 percent overhead comes from: four output characters for every three input bytes means the output length is 4/3 times the input length.

How the Encoding Works

The encoder reads the input three bytes at a time, treats them as a single 24-bit buffer, and slices that buffer into four 6-bit groups. Each 6-bit group indexes into the 64-character alphabet to produce one output character. If the input length is not a multiple of three, padding with = brings the output up to a multiple of four characters.

Concretely, the ASCII string "Man" is three bytes: 0x4D, 0x61, 0x6E. As a 24-bit buffer, that is 01001101 01100001 01101110. Split into four 6-bit groups: 010011, 010110, 000101, 101110, which are 19, 22, 5, and 46 in decimal. Indexing into the alphabet (A=0, B=1, ..., Z=25, a=26, ..., z=51, 0=52, ..., 9=61, +=62, /=63) yields T, W, F, u — so "Man" encodes as TWFu. RFC 4648 contains a longer worked example that is worth running by hand once.

If the input length is one byte short of a multiple of three, two padding characters are added; if it is two bytes short, one padding character is added. The input "Ma" encodes to TWE=, and the input "M" encodes to TQ==. Decoders use the padding to recover the exact original length.

The base64url Variant

Standard Base64 uses + and /, both of which have special meaning in URLs (where + is decoded as a space by some form parsers and / separates path segments). RFC 4648 defines a URL-safe variant, base64url, that replaces + with - and / with _. Padding is typically omitted in base64url because the length can be inferred from context. JWTs use base64url, and so do most modern OAuth tokens, OpenID Connect identifiers, and AWS signed URL schemes.

If you ever need to convert standard Base64 to base64url by hand, replace + with - and / with _, and strip trailing = characters. The reverse conversion adds the = padding back by counting characters to the next multiple of four.

Common Use Cases

  • Email attachments (MIME). SMTP was originally designed for ASCII text. RFC 2045's MIME standard uses Base64 to transport binary attachments through SMTP relays that would otherwise mangle non-ASCII bytes.
  • Data URIs in HTML and CSS. Small images can be inlined directly into HTML or CSS as data:image/png;base64,.... This eliminates an extra HTTP request but inflates the file by 33 percent and disables browser caching of the image separately from the document. Use sparingly — usually only for icons under a few kilobytes.
  • JWT payloads. Per RFC 7519, the header, payload, and signature of a JWT are each base64url-encoded and joined with periods. This allows a JWT to be carried in an HTTP header without escaping.
  • API keys and tokens. Many APIs issue opaque tokens that are simply random bytes, base64url-encoded for transportability. Stripe's publishable and secret keys, GitHub personal access tokens, and Slack webhook URLs all rely on Base64-family encodings.
  • Embedding binary blobs in JSON. JSON cannot carry raw bytes. Small binary objects (cryptographic hashes, certificates, compressed payloads) are base64-encoded into a string field.

When NOT to Use Base64

Base64 is the wrong tool whenever you control both ends of the wire and can transmit raw bytes. A 10 MB image uploaded via multipart form-data reaches the server as 10 MB; the same image embedded as a Base64 string in a JSON body is 13.3 MB, inflating bandwidth and parse time for no benefit. Browser images that can be cached separately should be separate resources, not inlined data URIs. Large binary payloads in JSON should be reconsidered — multipart uploads, presigned S3 URLs, or a binary protocol like gRPC are usually better.

Base64 also offers no confidentiality. Anyone who intercepts a Base64-encoded string can decode it in milliseconds; the encoding is publicly specified and there is no key. Never confuse "Base64-encoded" with "encrypted." The same goes for JWTs: a JWT's payload is base64url-encoded, not encrypted, and any party that receives the token can read its contents.

Implementation Notes

In JavaScript, use btoa and atob for ASCII strings (the names stand for "binary to ASCII" and back). For arbitrary byte arrays, use Buffer.from(bytes).toString('base64') in Node or the modern FileReader.readAsDataURL / btoa(String.fromCharCode(...new Uint8Array(arrayBuffer))) pattern in the browser. In Python, base64.b64encode and base64.b64decode handle standard Base64; base64.urlsafe_b64encode and base64.urlsafe_b64decode handle base64url. Always validate the input length is a multiple of four (after stripping any whitespace) before decoding, and consider the validate=True argument in Python's b64decode if you need to reject non-canonical encodings.

Common Implementation Pitfalls

Several bugs recur across codebases that handle Base64. The first is failing to handle padding correctly — some URL-safe variants omit the trailing = characters, but standard decoders may reject unpadded input, and adding padding back requires counting to the next multiple of four. The second is character substitution: stripping whitespace from the input is fine, but stripping newlines that some encoders insert every 76 characters (per RFC 2045's MIME convention) requires explicit handling. The third is confusing standard and URL-safe alphabets: a token signed with + and / will fail to verify if the verifier uses the URL-safe alphabet, and the failure mode is silent rejection rather than an explicit error.

The 76-character line-wrap convention, in particular, catches teams off guard. PEM-encoded certificates, OpenSSL output, and some legacy mail encoders wrap Base64 output at 76 characters per line. Decoders in modern languages (Node's Buffer.from, Python's b64decode with validate=False) tolerate this whitespace, but strict decoders may not. If you are interoperating with a system that expects unwrapped Base64, strip newlines before transmitting.

Related Encodings

Base64 is not the only binary-to-text encoding. RFC 4648 also defines Base32 (using A-Z and 2-7, with 5 bits per character and an 8-to-5 byte-to-char ratio), which trades compactness for case-insensitive readability and is used in DNSSEC, OTP secret sharing, and some hardware token enrollment schemes. Base16 (hexadecimal, defined in the same RFC) is the most conservative choice — two characters per byte, but perfectly readable and unambiguous. Ascii85 (used in PostScript and PDF) encodes 4 bytes as 5 characters, reducing overhead to 25 percent, but at the cost of an alphabet that includes characters needing URL encoding.

For most web work, Base64 and its URL-safe variant are the right choices; reach for Base32 when case-insensitive transport matters, and for hexadecimal when human readability of the encoded output is more important than size. Each encoding has a precise spec; choose the one that matches your transport constraints, and never invent a custom variant — the standard alphabets exist precisely so that independent implementations can interoperate.

Conclusion

Base64 is a 3-bytes-to-4-characters encoding that lets binary data cross text-only boundaries. Its alphabet, padding rules, and base64url variant are fully specified in RFC 4648, and the encoding is universal: every programming language has a standard library implementation. Use it for small binary blobs in JSON, for email attachments, for JWTs, and for opaque API tokens. Do not use it for large files, for inlinable browser assets that would be better cached separately, or as a substitute for encryption. The 33 percent size overhead is the price of admission; it is worth paying when the only alternative is breaking the wire protocol, and not worth paying when you can simply send the raw bytes. Written by the HT99 Tools Editorial Team.

Try the Tool This Article Explains

Put what you've learned into practice with our free, accurate calculators.

Browse All Tools → More Articles