URL Encoder/Decoder

Encode and decode URL strings

A URL may only carry a small set of ASCII characters literally; everything else has to be converted to UTF-8 bytes and written out as %XX percent-encoding. This tool does both directions: turn text into something safe to drop into a query string, or turn %E4%B8%AD%E6%96%87 back into readable characters. It runs on the browser's built-in encodeURIComponent and decodeURIComponent, entirely on your device, and nothing you paste leaves it — which matters, because the strings people need to decode are usually callback URLs carrying tokens, signatures, or user email addresses.

How to use it

  1. Pick a direction with the two tabs at the top: Encode turns raw text into percent-encoding, Decode reverses it. Switching tabs clears both boxes on purpose, so an encoded result never gets mistaken for pending decode input.
  2. Paste into the input box. Its label changes with the mode — "raw text" when encoding, "URL-encoded string" when decoding.
  3. Press the action button (it reads Encode or Decode depending on the mode). Nothing converts as you type; the button is what runs it.
  4. The result appears in the box below; take it with the copy button above it. The third button clears the input, the result, and any error in one go.
  5. A failed decode leaves the result empty and shows an "invalid URL-encoded string" message underneath. That means the input contains an illegal percent sequence — see the notes for the four usual causes.
  6. To round-trip a value (encode, then decode it back to confirm), copy the result, switch modes, and paste it in. The two modes share one set of boxes and do not hand off automatically.

When it comes in handy

Build a query-string value that survives transit

Putting Taipei 101 & nearby into ?q= without encoding lets the & act as a parameter separator, so the second half silently becomes a different parameter. Encoded, the whole thing stays one value.

Read what is actually inside an OAuth or payment callback

Identity providers and payment gateways stuff whole URLs into redirect_uri or state, so your logs show https%3A%2F%2Fapp.example.com%2Fauth%2Fcallback%3Fnext%3D%252Forders. Decoding reveals the real destination — and the %252F in there tells you something got encoded twice.

Split the blame on "non-ASCII params arrive as mojibake"

Paste the exact string the client sent into Decode. If it comes back correct, the bug is on the server side (its decoding or the database charset). If it errors out or produces replacement characters here, the bytes on the wire were never valid UTF-8 and the sender is at fault. Either way you have halved the search space.

Hand-write a curl or Postman request

Manual API calls fall over on values containing +, =, or / — Base64 blobs and JWTs being the usual suspects. Encode the value here first and you eliminate "URL syntax ate my parameter" as a variable.

Limits and behavior

  • This uses encodeURIComponent, which is built for one *component* of a URL, not a whole URL. That means :, /, ?, #, &, and = all get escaped. Paste https://a.com/b?c=d and you get https%3A%2F%2Fa.com%2Fb%3Fc%3Dd — exactly right when the URL is itself a parameter value (a redirect_uri, say), and far too aggressive when you only wanted to fix the non-ASCII bits while keeping :// and ? intact. That second job belongs to encodeURI, which this tool does not offer; the correct fix is to encode each parameter value separately and assemble the URL yourself.
  • Spaces always become %20, never +. The +-means-space convention belongs to application/x-www-form-urlencoded — what HTML form GETs emit, and what PHP, Java Servlets, and plenty of other query parsers assume — not to URL syntax itself. So if you are imitating a form submission, swap %20 for + yourself. Going the other way, a+b decodes to a literal a+b, not a b, because decodeURIComponent knows nothing about that convention.
  • Which is why a literal + must be encoded as %2B. An unencoded ?email=a+b@example.com is read as a b@example.com by any form-urlencoded parser. It is the most common and least visible way to corrupt user data in a query string.
  • Seventy-one characters pass through untouched: A–Z a–z 0–9 - _ . ! ~ * ' ( ). The first 66 are RFC 3986 unreserved characters and are genuinely safe, but !, *, ', (, and ) are sub-delims under RFC 3986 — encodeURIComponent still follows the older RFC 2396 and leaves them alone. Harmless most of the time, fatal when you are building an OAuth 1.0 signature base string or an AWS SigV4 canonical query: there you must replace them by hand with %21, %2A, %27, %28, %29 or the signature will not verify.
  • Non-ASCII text is converted to UTF-8 and encoded byte by byte, so length grows fast: a CJK character becomes 9 characters (%E4%B8%AD), a basic emoji 12 (😀%F0%9F%98%80), and a ZWJ sequence such as a family emoji 36 or more. Between the legacy 2,083-character URL ceiling and the default header-size limits in nginx and most CDNs, non-ASCII parameters hit the wall sooner than people expect.
  • Percent-encoding is all this tool does. It does not parse URL structure, does not convert hostnames (internationalized domains use punycode xn--, not percent-encoding), does not do Base64, and will not guess how many layers you need. Each press handles exactly one layer.

Frequently asked questions

What triggers the "invalid URL-encoded string" error?

decodeURIComponent throws a URIError on any malformed percent sequence. Four cases cover nearly all of it: a bare % (pasting 100% off into Decode does this); a % not followed by two hex digits, like %ZZ; a truncated sequence, where a copy-paste stopped mid-%E4; and byte sequences that are not valid UTF-8, typically a string from a legacy system that encoded in Big5 or Latin-1. That last case is unrecoverable here — you need a tool that lets you name the source charset.

I decoded it and still see `%25` or `%2520`. Now what?

It was encoded twice. % itself encodes to %25, so a second pass turns %20 into %2520. Seeing %25 is the fingerprint. Copy the result, switch to Decode, paste, and press again — each press peels off one layer. The real fix is upstream: find the code path that encodes an already-encoded value, usually where hand-built encoding meets a framework or HTTP client that encodes for you.

Is anything I paste uploaded?

No. Conversion is encodeURIComponent and decodeURIComponent running in your browser, and it finishes there; no request carries what you paste anywhere. Disconnect from the network and try again to confirm; the tool keeps working. Callback URLs containing an access_token or signature are safe to paste. (The page itself loads third-party resources such as ads; those requests contain none of your input.)

This outputs uppercase `%E4` but another tool gave me `%e4`. Which is right?

Both, and they are equivalent. RFC 3986 recommends uppercase and encodeURIComponent always emits uppercase; decoders accept either case. The one place it matters is signing and cache keys: if anything downstream hashes or string-compares the encoded form, the two cases are two different values.

Do I really need to encode non-ASCII? Browsers seem to handle it.

The address bar displays non-ASCII characters, but the request on the wire still carries the encoded bytes — that is presentation, not protocol. You get no such help when you concatenate strings in code, drive an HTTP client, or configure a redirect. Unencoded non-ASCII can be rejected outright by a proxy, CDN, or WAF in the middle, or decoded with a guessed charset. Encode before it goes into a URL.

How is this different from Base64? Both are called encoding.

Different purposes. Percent-encoding exists to neutralize characters that have meaning in URL syntax; the output stays mostly human-readable and only the escaped parts grow. Base64 represents arbitrary binary data using 64 safe characters; the output is unreadable and always about 33% larger. Use percent-encoding to put text in a query string, Base64 to put a binary payload in a text field. Neither is encryption and neither hides anything.