Free — no signup

Base64, URL, HTML & Hex Encoder / Decoder

Paste anything. Every encoding is computed in both directions at once — there is no “encode” or “decode” button to choose, so a pasted token tells you what it encodes to and what it decodes from, in the same view.

Base64 and base64url, URL/percent, HTML entities, hex and JSON/Unicode escapes — plus MD5, SHA-1, SHA-256 and the exact bytes in hex, decimal, octal and binary.

Everything this tool converts

0 characters · 0 bytes
Base64 standard alphabet, padded — RFC 4648 §4
Base64 decoded your input read as Base64

base64url URL-safe, unpadded — RFC 4648 §5, the JWT form
URL-encoded form encoding — space becomes +
URL-decoded percent-decoded; + is left alone
HTML-encoded safe to drop into markup or an attribute
HTML-decoded entities resolved back to characters
Hex contiguous lowercase, the xxd -p form
Hex decoded your input read as hex

JSON escaped quoted, ASCII-safe \uXXXX — paste-ready
JSON unescaped your input read as a JSON string

SHA-256 use this one for integrity
SHA-1 legacy checksums only — collision-broken
MD5 legacy checksums only — collision-broken
  Raw bytes and non-cryptographic hash
Hexadecimal
Decimal
Octal
Binary unsigned, eight digits per byte
FNV-1a 64 non-cryptographic — bucketing and change detection

Byte values are shown signed, in the range −128 to 127, so 0xFF reads as -1. That is the convention Java and Kotlin use for a byte, and it is why a high byte appears with a minus sign rather than as 255.

What each field means

FieldWhat it is, and when you want it
Base64 Your input's bytes in the standard RFC 4648 §4 alphabet, using + and / and padded to a multiple of four with =. This is what MIME email, data URIs and HTTP Basic authentication use.
Base64 decoded Your input read as Base64 rather than as text. Empty is ambiguous on its own, so this page says which of the three cases you hit: not Base64 at all, Base64 of nothing, or valid Base64 whose bytes are not readable text.
base64url The same bytes in the URL- and filename-safe alphabet (RFC 4648 §5), with - and _ replacing + and /, and padding omitted. This is the form JWT and the rest of JOSE (RFC 7515) require.
URL-encoded application/x-www-form-urlencoded: unreserved characters pass through, a space becomes +, and everything else becomes %XX with uppercase hex. This is what a browser sends for a form field or a query parameter.
URL-decoded Percent-decoding only: %C3%A4 becomes ä, while a + stays a +. Leaving + alone is deliberate — see the section below.
HTML-encoded The five characters that can break out of markup — &, <, >, ", ' — replaced by character references, so the value is safe inside element text or a quoted attribute. The apostrophe uses numeric &#39;, because HTML 4 defines no &apos;.
HTML-decoded Character references resolved back to text: every numeric form (&#169;, &#xA9;) plus the HTML 4.01 Latin-1, special and common symbol names. An & that starts nothing recognisable is left exactly where it is, so “Tom & Jerry” survives a decode intact.
Hex The bytes as one contiguous lowercase string — what xxd -p, checksums and wire dumps use. Not to be confused with the bracketed byte view further down, which is a different rendering of the same bytes.
Hex decoded Your input read as hex. Whitespace, a leading 0x, and : or - separators are ignored, so a certificate fingerprint or a MAC address pastes in as-is. An odd number of digits is not a byte string and is reported as invalid.
JSON escaped A complete, quoted JSON string literal — quotes included, so it pastes straight into JSON, JavaScript or Java. Deliberately ASCII-safe: every non-ASCII character becomes \uXXXX (a surrogate pair above U+FFFF), which is what survives a latin-1 pipe or a terminal that cannot draw the glyph.
JSON unescaped Your input read as a JSON string literal, with \n, \t and \uXXXX resolved. The surrounding quotes are optional. A raw newline or an unpaired quote makes it malformed, and the page says so rather than guessing.
SHA-256 The digest to reach for: content addressing, integrity checks, cache keys, signatures. 64 lowercase hex characters.
SHA-1, MD5 Both are collision-broken and must not be used anywhere an attacker picks the input. They remain useful for reading legacy checksums, Git object ids, ETags and vendor APIs that still publish them.
Bytes, FNV-1a The exact bytes under UTF-8 in hex, decimal, octal and binary, plus a fast non-cryptographic 64-bit hash for bucketing and change detection. The first three are signed, Java/Kotlin style; binary is unsigned, because “text to binary” never means a negative byte.

+ or %20? Both, and the difference matters

This is the single most common way URL encoding goes wrong, and it is not a disagreement about style — there are genuinely two rules.

So a+b in a query string means a b, while a+b in a path means a+b. The failure is silent: nothing errors, your data is simply different at the other end. That is why the decode direction on this page percent-decodes only. It never turns a + into a space, because it cannot know which of the two contexts your string came from — and guessing wrong would corrupt exactly the input you came here to check.

HTML encoding is an output rule, not an input filter

Escaping &, <, >, " and ' is what stops a value being read as markup instead of as text — the whole of cross-site scripting, in one sentence. The part people get wrong is when: escape at the moment you write into the page, not when you accept the input. Escaping on the way in double-escapes anything shown twice and still leaves you exposed the moment a value reaches the page by another route.

The context matters too. This tool produces the escaping that is correct for element text and quoted attribute values, which is the overwhelming majority of cases. It is not sufficient inside a <script> block, inside a style attribute, or in an unquoted attribute — those need their own rules, and an HTML escaper is the wrong tool for them. Reach for your framework's contextual auto-escaping first; use this to check what it produced, or to escape a value by hand when nothing else will.

Base64 is not encryption

It has no key. Anyone holding the string can reverse it in one step — this page does it while you type. Base64 exists to move arbitrary bytes through channels that only carry text: email bodies, JSON string fields, data URIs, HTTP headers.

The practical consequence: a token, password or key that is “Base64 encoded” is not protected in any sense. If you find credentials stored that way, treat them as plaintext, and rotate them if they have been anywhere they should not have been.

The same thing from your terminal

This page is a front end for one public endpoint. It takes a value and returns every field above as JSON, so it drops straight into a script or a CI step.

curl 'https://api.lifub.com/encoding.json?value=Hello%20World'

# one field at a time, with jq
curl -s 'https://api.lifub.com/encoding.json?value=Hello%20World' | jq -r .base64Encoded
curl -s 'https://api.lifub.com/encoding.json?value=Hello%20World' | jq -r .hexEncoded
curl -s 'https://api.lifub.com/encoding.json?value=Hello%20World' | jq -r .jsonEscaped

# decode instead - the same call answers every direction at once
curl -s 'https://api.lifub.com/encoding.json?value=SGVsbG8gV29ybGQ%3D' | jq -r .base64Decoded
curl -s 'https://api.lifub.com/encoding.json?value=48656c6c6f'        | jq -r .hexDecoded
curl -s 'https://api.lifub.com/encoding.json?value=Tom%20%26amp%3B%20Jerry' | jq -r .htmlDecoded

# is this Base64, or hex, or neither? the flags say so
curl -s 'https://api.lifub.com/encoding.json?value=deadbeef' \
  | jq '{base64Valid, base64DecodedBytes, hexValid, hexDecodedBytes}'

Anonymous use is rate-limited per IP so the endpoint stays a usable public playground. The full response schema is in the API reference.

Your shell already ships offline equivalents worth knowing: base64 and base64 -d, xxd -p and xxd -r -p, jq -Rs . for JSON escaping, shasum -a 256, and md5sum. Use those when the input is a file, is large, or must never leave your machine.

Questions people actually ask

Is Base64 encryption?

No. Base64 is an encoding, not encryption. There is no key, and anyone who has the string can reverse it in one step — this page does exactly that. Base64 exists to carry arbitrary bytes through channels that only accept text, such as email bodies, JSON fields and data URIs. Never use it to hide a secret.

What is the difference between Base64 and base64url?

They encode identical bytes and differ in two characters and in padding. Standard Base64 (RFC 4648 section 4) uses + and / and pads with =. base64url (RFC 4648 section 5) uses - and _ instead, so the result is safe inside a URL or a filename, and JWT/JOSE (RFC 7515) requires the padding to be omitted. This page shows both, so you can copy whichever the target format wants.

Why does URL encoding turn a space into + instead of %20?

Because there are two different rules. In application/x-www-form-urlencoded — HTML form submissions and query strings — a space is encoded as +. In a URL path, and under RFC 3986 generally, a space is %20. Both decode back to a space in their own context, but using the wrong one silently corrupts data: a literal + in form-encoded data must itself be escaped as %2B, or it comes back as a space.

Why does an ordinary word like hell decode as Base64?

Because it genuinely is valid Base64. Any run of four characters drawn from the Base64 alphabet decodes to three bytes, whether or not those bytes mean anything. The word hell decodes to three bytes that are not readable text, so this page reports it as valid Base64 carrying binary data rather than showing you an empty box or pretending the input was rejected.

When should I HTML-encode a value?

At the moment you write it into the page, not when you accept it. Escaping on input double-escapes anything displayed twice and still leaves you exposed if the value reaches the page by another route. The escaping here is correct for element text and quoted attribute values, which covers almost everything; inside a script block, a style attribute or an unquoted attribute you need different rules, and an HTML escaper is the wrong tool. Prefer your framework's contextual auto-escaping and use this to check what it produced.

Why does the hex field look different from the hex bytes further down?

They are two renderings of the same bytes. The Hex row is one contiguous lowercase string, which is what xxd -p, checksums and wire formats use and what people mean by text to hex. The byte view lower down is a bracketed, comma-separated list of signed values in the Java and Kotlin convention, where a high byte reads as a negative number. Both ship because both get asked for.

Why does JSON escaping turn accented characters into \uXXXX?

Because the escaped form is the one that survives. A literal accented character is perfectly legal in JSON, but it only arrives intact if every hop is UTF-8 clean; the \uXXXX form travels through a latin-1 pipe, a Java properties file, an old terminal and a copy-paste unharmed. Characters above U+FFFF are written as a surrogate pair, which is what RFC 8259 requires. The unescape direction reads either form.

Can I hash passwords with MD5 or SHA-256?

No. MD5 and SHA-1 are broken for collision resistance and must not be used where an attacker chooses the input. SHA-256 is sound as a digest but is far too fast to store passwords with: it is designed to be quick, which is exactly what a password hash must not be. Use a deliberately slow, salted password hash — argon2id, scrypt or bcrypt. The hashes here are for checksums, cache keys, integrity checks and fingerprints.

Is my input stored or logged?

No. The text is sent over HTTPS to the Lifub API, converted in memory and returned in the response; nothing is written to a database or a file. Access logs on both the edge and the API record the request path only — never the query string that carries your text. That said, this is a public service on someone else's machine: do not paste live production secrets into it, here or into any other online encoder.

How long can the input be?

The live view accepts up to 8,000 characters. The API is a plain GET, so your text travels in the query string and the whole request line has to fit — measured at roughly 16 KB against the public endpoint, which is why very long or heavily escaped input is refused with an explanation rather than truncated. For anything larger, run the conversion locally.

More free tools from Lifub