Base64 encode and decode

Convert text or a file to Base64 and back. Choose the standard or URL-safe alphabet, padding and line wrapping, see how UTF-8 and Latin-1 change the result, and get the exact position and reason when Base64 input is invalid. Everything runs in your browser and nothing is uploaded.

Encoding options

    Runs in your browser. Nothing is uploaded. The share link puts your text in the part of the address after #, which browsers do not send to servers; anyone you give the link to can read the text. Only your option choices are saved in this browser, never your text.

    How to use

    1. Pick Encode to turn text or a file into Base64, or Decode to turn Base64 back into bytes.
    2. Type or paste into the box, or switch to File and choose a file. Files up to 25 MB are read in your browser; they are not uploaded.
    3. When encoding text, leave Text to bytes on UTF-8 unless the receiving system specifically wants Latin-1. Choose the alphabet: standard (+ and /) for email, data URIs and most APIs, URL-safe (- and _) for URLs, file names, and JWTs. Switch padding off if the receiver expects none.
    4. Use Line breaks only when a format asks for them: 76 characters per line is the MIME rule (RFC 2045), 64 is the PEM rule (RFC 7468).
    5. When decoding, leave the alphabet on Auto-detect unless you want to test one alphabet. The tool decodes the bytes and then shows them as UTF-8 text, Latin-1 text or a hex dump. If the input is wrong, the red box gives the position, line and column, and the reason; Select the problem character jumps to it.
    6. Use Copy result, Download (decoded bytes are saved exactly, so binary files survive) or Use result as input to chain a decode after an encode.

    The strictness switches mirror the choices the standards leave open. Ignore whitespace follows RFC 2045 (decoders must skip line breaks). Require padding follows RFC 4648 section 3.2. Strict trailing bits follows section 3.5, which lets a decoder reject a last character whose unused bits are not zero.

    Worked examples

    Each value below is checked by an automated test that runs the same input through the tool; the expected strings were worked out by hand from the bit patterns first.

    Plain ASCII text

    Thirteen bytes make four full groups of three plus one leftover byte, so the output ends in ==. The groups are SGVs, bG8s, IFdv, cmxk and IQ==.

    Encode, UTF-8, standard alphabet, padding on.

    Input: Hello, World!

    Output: SGVsbG8sIFdvcmxkIQ==

    UTF-8 or Latin-1: same letter, different bytes

    The letter é is code point U+00E9. UTF-8 writes it as two bytes, C3 A9; Latin-1 writes it as the single byte E9. The Base64 differs accordingly, and a decoder that assumes the other encoding will misread it.

    Encode, UTF-8.

    Input: é

    Output: w6k=

    Encode, Latin-1.

    Input: é

    Output: 6Q==

    A character Latin-1 cannot hold

    The euro sign U+20AC is three bytes in UTF-8 (E2 82 AC) and does not exist in Latin-1.

    Encode, UTF-8 (3 bytes: E2 82 AC).

    Input: €

    Output: 4oKs

    Padding and the URL-safe alphabet

    RFC 4648 section 10 gives BASE64("fo") = "Zm8=". Without padding the same bytes are Zm8. The three bytes FB FF BF split into the 6-bit values 62, 63, 62, 63, which is exactly where the two alphabets differ.

    Encode, URL-safe, padding off. Padded this is "Zm8=" (RFC 4648 section 10).

    Input: fo

    Output: Zm8

    Encode, Latin-1, URL-safe. The standard alphabet gives "+/+/".

    Input: ûÿ¿ (Latin-1 bytes FB FF BF)

    Output: -_-_

    Decoding

    Decode, show as UTF-8.

    Input: 4oKsMTAw

    Text: €100

    Decode, auto alphabet, "Require padding" off.

    Input: Zm9vYg

    Text: foob

    What goes wrong

    These are the failures people actually hit. The messages are the tool's exact output.

    btoa() and Latin-1 refuse characters above U+00FF

    Browsers' btoa() does the same: it throws for any character above U+00FF. Here the Latin-1 option gives:

    Encode, Latin-1.

    Input: Price: 5€

    Error: Character 9 ("€", U+20AC) is above U+00FF and has no single-byte Latin-1 form. Use UTF-8, which can encode every character.

    A Base64URL string pasted into a standard-only decoder

    JWT parts, URL tokens and file names use - and _. A decoder fixed to the standard alphabet rejects them. Leave the alphabet on Auto-detect, or choose URL-safe.

    Decode, alphabet forced to Standard.

    Input: -_-_

    Error: Invalid character "-" (U+002D) at position 1.

    Padding stripped by a URL or a tool

    Browsers' atob() accepts this, because the WHATWG algorithm tolerates missing padding. Strict decoders do not. With "Require padding" on:

    Decode, "Require padding" on.

    Input: Zm9vYg

    Error: Padding is required but missing: 6 characters need 2 "=" at the end (RFC 4648 section 3.2).

    Wrong amount of padding

    Decode.

    Input: Zg=

    Error: The data has 2 characters followed by 1 "=", but 2 characters need 2 "=" to reach a multiple of 4.

    Two Base64 strings pasted together

    Concatenating two encoded strings is not the same as encoding the concatenated data. The first = ends the data, so anything after it is an error:

    Decode.

    Input: Zg==Zg==

    Error: The "=" at position 3 is followed by more data ("Z" (U+005A) at position 5). Padding may only appear at the very end.

    Truncated data (5 characters)

    Decode.

    Input: Zm9vY

    Error: 5 data characters is not a valid length. A group of 4 characters carries 3 bytes, 3 carry 2 bytes, 2 carry 1 byte, and 1 character on its own carries only 6 bits, which is less than one byte.

    A "+" that a URL turned into a space

    The standard alphabet contains +, and in a query string or HTML form a + is read as a space. If the receiver turned each plus into a space and you decode with "Ignore whitespace" on, the spaces vanish silently and the bytes are wrong. Turn "Ignore whitespace" off and the same text is reported: Found a space at position 1, but whitespace is not allowed in strict mode. Percent-encode the Base64 when it travels in a URL, or use the URL-safe alphabet.

    Decode, ignore whitespace on, show as hex. No error is raised. The correct bytes were FB FF BF; the result is a single byte, FF.

    Input: " / /" (the standard string "+/+/" after a query string replaced each + with a space)

    Output: one byte, FF (hex dump line: 00000000 ff ... .)

    Latin-1 bytes decoded as UTF-8

    Decode, show as UTF-8. The byte E9 is not valid UTF-8 on its own; the tool shows U+FFFD and says why.

    Input: 6Q==

    Result: � (U+FFFD, the replacement character) plus a note that byte 1 is not valid UTF-8

    Limits & gotchas

    • Size limits are this tool's, not the standard's. Files over 25 MB are refused, and only the first 1,000,000 characters of a result are displayed (Copy and Download still use all of it). The limits exist because everything is held in your browser's memory; the right numbers for your device may differ.
    • Base64 is not encryption and not compression. Output is 4 characters per 3 bytes, about a third larger than the input (RFC 4648 section 4), plus line breaks if you ask for them.
    • "Latin-1" here means byte value = code point. That is what btoa() and atob() do. The WHATWG Encoding Standard says the labels "latin1" and "iso-8859-1" mean windows-1252 in the web platform, where bytes 80 to 9F map to other characters (for example 80 is the euro sign). This tool does not apply windows-1252; if your data came from a Windows program, bytes in that range will not show as you expect.
    • Different decoders disagree on sloppy input. The WHATWG atob() algorithm removes whitespace, accepts missing padding and silently discards non-zero trailing bits, so YQ and YR both give "a". RFC 4648 says decoders must reject characters outside the alphabet unless the referring specification says otherwise, and RFC 2045 says MIME decoders ignore them. Node's Buffer.from(s, 'base64') skipped a stray ! when tested on Node 20.19.2; we did not find that documented, so treat it as observed behaviour. This page defaults to tolerant on whitespace, tolerant on padding, and lets you switch each one to strict.
    • Non-canonical last characters. Zh== and Zg== both decode to "f" (RFC 4648 section 3.5). By default we accept it and add a note; "Strict trailing bits" rejects it.
    • Other flavours are not covered. Base32, Base16 and the IMAP "modified base64" are different encodings. Radix-64 with a CRC (OpenPGP armor) is not handled.
    • Data URIs: a leading data:...;base64, is removed when decoding, and the tool says so. Nothing else about the media type is interpreted.
    • The share link contains your text in the address (after #), limited to about 3,000 characters. File contents are never put in a link. Do not share links containing secrets.

    FAQ

    Why does my Base64 string end in = or ==?

    Base64 turns every 3 bytes into 4 characters. When the data length is not a multiple of 3, the last group is short and RFC 4648 section 4 completes it with "=": one leftover byte gives two characters and "==", two leftover bytes give three characters and "=". The padding carries no data. It can be left out when the length is known some other way (RFC 4648 section 3.2), which is why JWTs and many URL tokens omit it.

    What is the difference between Base64 and Base64URL?

    Only two characters. The standard alphabet (RFC 4648 section 4) uses + and / for values 62 and 63; the URL and filename safe alphabet (section 5) uses - and _ instead, because + and / are special in URLs and file names. The same bytes therefore encode to different text, for example the three bytes FB FF BF are "+/+/" in one and "-_-_" in the other. Pick the alphabet the receiving system expects.

    Why does btoa() throw "Invalid character" on some text?

    btoa() treats each character as one byte and throws an InvalidCharacterError for any character above U+00FF, such as the euro sign or an emoji (HTML Standard). To encode arbitrary text, turn it into UTF-8 bytes first and encode those, which is what this page does by default. If you pick Latin-1 here you get the same restriction, with a message that names the character and its position.

    My decoded text shows � or strange characters like é. What happened?

    Base64 carries bytes, not text. The text only appears once the bytes are read with an encoding. "é" is what the two UTF-8 bytes C3 A9 look like when read as Latin-1; "�" is shown when bytes are not valid UTF-8 (for example a lone E9 from Latin-1 text). Switch "Show the bytes as" between UTF-8, Latin-1 and Hex dump to see which reading makes sense. If the bytes are an image or a compressed file, no text reading will.

    Is Base64 encryption?

    No. Anyone can decode it with no key, as this page shows. It only changes how bytes are written so they survive systems that expect text. It adds roughly one third more size: 4 characters for every 3 bytes (RFC 4648 section 4). Do not use it to hide a password or token.

    Sources

    Every document above was opened and read on 2026-10-02. Documentation changes; if a page here disagrees with the current docs, trust the docs and tell us.