Percent-encode and decode against four character sets, build query strings, and take a URL apart
Initializing in your browser…
Encode and decode text, files and JSON as base64, base64url or hex, checked by decoding the result back
Decode a JSON Web Token and, given the secret or public key, actually verify its signature. Refuses alg: none and algorithm confusion
Convert between Unix timestamps and dates in any of 400+ time zones, in seconds, milliseconds, microseconds or nanoseconds
A signature keeps failing against a server that says it follows RFC 3986, and the value you are signing has an apostrophe and brackets in it.
Value to encode
it's (50% off) ~ now!
The same 21 bytes in all four sets
RFC 3986 it%27s%20%2850%25%20off%29%20~%20now%21 Component it's%20(50%25%20off)%20~%20now! Full URI it's%20(50%25%20off)%20~%20now! Form data it%27s+%2850%25+off%29+%7E+now%21
The four disagree, and that is the whole point. encodeURIComponent leaves ! ' ( ) * alone although RFC 3986 lists them as reserved, so a signature computed with it will not match a server that percent-encodes them. The form serializer that URLSearchParams implements writes a space as + and also escapes ~, which the other three leave alone. The tool shows all four side by side for whatever you type, names which reference each one is checked against, and decodes its own output back to confirm the bytes survived.
A URL carries only ASCII, so every other octet has to be written as `%XX`, the hex of one UTF-8 byte. A space becomes `%20`, an ampersand inside a value becomes `%26`, and a character like `é` becomes `%C3%A9` because UTF-8 encodes it as two bytes. Without that, `?name=alice&smith` parses as two parameters and the name is lost. The part people get wrong is not the mechanism, it is which characters to escape, because there is no single answer. RFC 3986 says only the unreserved set survives: letters, digits, and `-` `.` `_` `~`. JavaScript’s encodeURIComponent additionally leaves `!` `'` `(` `)` `*` alone, which the RFC lists as reserved sub-delimiters. encodeURI leaves the structural characters `; / ? : @ & = + $ , #` alone as well, so it is for a whole URL and never for one value. The application/x-www-form-urlencoded serializer that URLSearchParams implements writes a space as `+` and escapes `~` too. This tool implements all four and shows the same input in every one of them at once, so the differences are visible rather than something to take on trust.
Each set is checked against an implementation that is not this tool: the component set against encodeURIComponent, the full URI set against encodeURI, the form set against URLSearchParams, and the RFC 3986 set against the unreserved list written out from section 2.3 of the standard. All four agree with their reference on every printable ASCII character and on a corpus of Unicode strings. When you encode something the tool decodes its own output back and states on screen whether the bytes matched, so the green line is a measurement rather than a claim.
Decoding says what is wrong instead of giving up. `decodeURIComponent` throws a single "URI malformed" for `%zz`, for a truncated `%2` at the end, and for escapes that decode to bytes that are not UTF-8, which tells you nothing about where the problem is. This decoder names each problem, gives its index in the input, still produces the bytes it could read, and prints them as hex, so a value carrying non-UTF-8 bytes is visible as bytes rather than as a row of replacement characters. Whether `+` means a space is a choice, not a fact: a form-urlencoded reader treats it as one and a path reader does not, so the decode panel offers both and the analyzer shows both readings for any value that contains one.
Analyze takes a URL apart and reads every value exactly once. That sounds obvious and is the usual bug: `URLSearchParams` has already percent-decoded and already turned `+` into a space by the time you read a value from it, so running a decoder over that value again turns `%2520` into a space that was never in the data, and throws outright on a value that legitimately contains a percent sign. The raw text and the decoded value are shown side by side for every parameter, every path segment and the fragment, and each parameter is checked to match what `URLSearchParams` hands a server.
Base64 URL is a different job and is offered separately: the UTF-8 bytes written in the RFC 4648 section 5 alphabet, with `-` and `_` in place of `+` and `/` and the padding dropped. It is for an opaque token that has to survive a URL, not for readable text. Note that it encodes the UTF-8 bytes; a Latin-1 based encoder gives a different and wrong answer for anything above U+007F.
OAuth 1.0a and several AWS signing schemes specify RFC 3986 encoding. encodeURIComponent leaves five characters unescaped that those specifications escape, which is a silent signature mismatch. The RFC 3986 set produces the canonical form.
Paste it into Analyze to see every parameter as both the raw text and the value a server reads, with any broken escape pointed at.
A value like a+b means two different things depending on who reads it. The analyzer shows both readings for any value containing one.
Accented, CJK, Cyrillic and emoji input is encoded from its UTF-8 bytes in every set, and the round-trip check confirms the bytes survived.
encodeURI leaves the structural characters ; / ? : @ & = + $ , # alone, so it is for a whole URL whose structure is already correct. encodeURIComponent escapes them, so it is for a single value. Using the first where you needed the second produces a URL that looks right and parses wrong.
On exactly five characters: ! ' ( ) and *. RFC 3986 lists them as reserved sub-delimiters and escapes them; encodeURIComponent leaves them alone. That difference is what breaks a signature computed with the wrong one.
A form submission uses + because the application/x-www-form-urlencoded serializer says so; everywhere else a space is %20. Both are correct in their own context, which is why the decode panel lets you say which one you are reading.
Decoding still produces the bytes and shows them as hex, and says on screen that the text above them contains replacement characters and will not re-encode to the same bytes. A decoder that silently hands you the replacement characters has changed your data.
No. Everything runs in the page, and the text being converted is deliberately kept out of the address bar and out of any share link, so it does not end up in your history or in a link you paste to someone.
Conversions run on your device in JavaScript. The values you enter are never sent over the network.