Skip to main content
ToolMaple

URL Encoder and Decoder

Encode or decode percent-encoded URLs; choose encodeURI or encodeURIComponent.

Last updated:

Whole URL keeps the structural characters : / ? & = # intact, for a link that has to stay clickable. Single value handles those too, for one query parameter or path segment.

Result
https://www.toolmaple.com/?q=caf%C3%A9%20%F0%9F%8D%81&page=1

Why a URL cannot hold a space, an accent or an emoji

RFC 3986, the specification every browser and server follows, allows only a narrow set of ASCII characters in a URL: the letters A-Z and a-z, the digits 0-9, the four unreserved marks - . _ ~, and a short list of reserved characters such as / ? # & = : that exist to mark where one part of the address ends and the next begins. Nothing else is allowed on the wire.

Percent-encoding is how everything else gets through. The text is converted to bytes using UTF-8, then each byte that is not allowed becomes a percent sign and two hexadecimal digits. A space is one byte and becomes %20. The letter é is two bytes in UTF-8 and becomes %C3%A9. A maple leaf, 🍁, is four bytes and becomes %F0%9F%8D%81. Most Japanese, Korean and Chinese characters are three bytes, so one character turns into nine URL characters.

The reason to do this deliberately, rather than pasting raw text and hoping, is that the failures are quiet. A space in the middle of a link makes a chat client, a Markdown parser or a PDF viewer cut the link short, and the recipient gets half an address with no error message. Worse, an unescaped & inside a value does not break anything visibly: the server simply reads it as a parameter separator, so a search for fish & chips arrives as a search for fish plus a mystery parameter called chips. Nothing throws, the data is just wrong. An unescaped # is the same story: everything after it never reaches the server at all, because the fragment is a client-side concept.

Which mode to use, and which encoder your code should call

Two choices decide what happens: Mode says whether you are going in or coming out, and Scope says how much of the string is allowed to keep its structure. Pick by what the string is, not by what it looks like:

  • Encode: you are building a link by hand and it contains a space, an accented letter, an emoji or non-Latin text. Typical cases are a search link you want to paste into a ticket, a mailto: address with a subject line, or a redirect target.
  • Decode: you have a wall of %XX from a server log, an analytics report, an email tracking link or a browser address bar, and you want to see what it actually says.
  • Whole URL, which runs encodeURI and decodeURI: the entire address is the input, and the structural characters : / ? # & = + $ , ; @ are left alone so the link still works as a link. This is the default.
  • Single value, which runs encodeURIComponent and decodeURIComponent: the input is one value, such as a query parameter or one path segment. It escapes the structural characters too, which is the only way to put a full URL inside another URL.
  • URLSearchParams or your language equivalent: you are assembling a query string from several key and value pairs. Let it do the joining and the escaping instead of concatenating strings; it is the option least likely to be wrong.

If you are unsure which scope you need, ask whether the string should be able to change the shape of the URL. A value typed by a user never should, so it gets Single value.

How to encode a link before you share it

  1. Leave the mode on Encode and the scope on Whole URL.
  2. Paste the full address into the box, replacing the sample. Include the scheme, https://, so the structure is visible.
  3. Read the result. Every space should now be %20 and every accented or non-Latin character a run of %XX. The slashes, the question mark and the ampersands between parameters should be untouched: that is what tells you the link is still a link.
  4. Press Copy and paste it into a browser address bar once before you send it. A link that opens for you is the only test that matters.

One case needs a different approach: if the value you are inserting is itself a URL, for example a redirect_uri or a next= parameter, encoding the whole address is not enough. The inner address keeps its own slashes and question mark, and the server reads them as part of the outer URL. Encode the inner address on its own with the scope switched to Single value, which runs encodeURIComponent, then paste the escaped result into the outer link.

How to read a percent-encoded line from a log

  1. Switch the mode to Decode.
  2. Paste the encoded string. Copy the whole path and query together rather than a fragment of it, because a cut in the middle of a %XX sequence makes the decode fail.
  3. If escapes such as %3D, %26 or %2F survive untouched, that is decodeURI protecting the URL structure. Switch the scope to Single value to decode those as well, which is what you want when the string was one parameter rather than a whole address.
  4. If the result still contains %25 sequences, the value was encoded twice. Copy the output back into the box and decode once more.
  5. If you get URI malformed, look for a bare percent sign that was meant literally, or a truncated escape at the end of the line.
  6. If the text decodes but reads as nonsense letters, the bytes were not UTF-8. See the caveats at the end of this page.

Decoding is also the fastest way to answer “what did the user actually search for” from an access log, and to check whether a client encoded a token or a signature before sending it. If the decoded value turns out to be JSON, the JSON formatter will pretty-print it; if it turns out to be Base64, the Base64 decoder takes it from there.

Getting the result where it needs to go

The Copy button puts the result on your clipboard. Where it lands next decides whether anything else has to change:

  • Into HTML: an encoded URL can still contain & between parameters, and inside an href attribute that has to be written &. This is HTML escaping, a separate step from percent-encoding.
  • Into a terminal: wrap the URL in single quotes. An unquoted & sends the command to the background, ? and * may be expanded by the shell, and # starts a comment.
  • Into a spreadsheet: a long percent-encoded string is treated as text, but Sheets and Excel may try to linkify it. Format the column as plain text first if you are going to compare values.
  • Into a chat or a document: Slack, Teams, Notion and most Markdown editors autolink up to the first space, which is exactly why the encoded form travels safely and the raw one does not.
  • Onto a phone: on iOS press and hold in the field and tap Paste; on Android press and hold and choose Paste. For handing a link to someone standing next to you, a QR code is easier than reading out forty percent escapes.

The same job in other languages and runtimes

Every language ships two or three encoders, and the split is almost always the same: one follows RFC 3986 and writes a space as %20, the other follows the older application/x-www-form-urlencoded rules and writes a space as +. In JavaScript:

encodeURI('https://example.com/a b?q=café')
// https://example.com/a%20b?q=caf%C3%A9

encodeURIComponent('fish & chips')
// fish%20%26%20chips

new URLSearchParams({ q: 'fish & chips', page: '1' }).toString()
// q=fish+%26+chips&page=1

Note the last line: URLSearchParams serializes a space as +, because it implements the form-encoded format rather than plain percent-encoding. That is correct for a query string and wrong for a path segment.

# Python
from urllib.parse import quote, quote_plus, unquote
quote('a b/c')            # 'a%20b/c'    slash is safe by default
quote('a b/c', safe='')   # 'a%20b%2Fc'
quote_plus('a b/c')       # 'a+b%2Fc'
unquote('caf%C3%A9')      # 'café'
  • PHP: rawurlencode() follows RFC 3986 and gives %20; urlencode() is the form variant and gives +.
  • Java: java.net.URLEncoder.encode() is form encoding despite the name, so it produces + for a space. It is the wrong tool for a path segment; build the URL with java.net.URI instead.
  • Go: url.QueryEscape() gives +, url.PathEscape() gives %20.
  • curl: curl -G https://example.com/s --data-urlencode "q=fish & chips" builds the query string for you, which is safer than escaping by hand in a shell.

Decoding has the same split, and this is where bugs hide: decodeURIComponent in JavaScript and unquote in Python turn %20 back into a space but leave + as a literal plus sign. If a value arrives from an HTML form and you decode it with the RFC 3986 decoder, every space comes out as a plus. Use URLSearchParams or unquote_plus for anything that came from a form submission.

Limits, character sets and what this page does not do

What leaves your browser: nothing. The conversion runs locally through the built-in encodeURI, encodeURIComponent, decodeURI and decodeURIComponent functions. Your input is not sent to a server, not stored and not logged, and both the box and the result are masked so they cannot appear in analytics session recordings.

UTF-8 is assumed. Percent escapes carry bytes, not characters, and the specification does not record which character set produced them. Everything modern uses UTF-8, which is what this page decodes. Escapes generated by an older system using Shift_JIS, GBK, Big5 or Windows-1252 will decode into mojibake or fail, and recovering them needs a decoder that lets you name the original encoding.

Encoding is not sanitizing. Percent-encoding makes a string safe to transport in a URL. It is not HTML escaping, it is not SQL escaping, and it is not a defence against injection. A value that arrives encoded still has to be validated after it is decoded, and an open redirect is not prevented by escaping the destination.

Length has no specified limit, but every hop has one. Browsers, web servers, proxies and log pipelines each impose their own maximum request line, and the smallest one in the chain decides. Server defaults commonly sit around 8 KB at the time of writing, and some gateways cut in earlier, so keeping a query string under about 2 KB is a practical rule of thumb. Anything larger belongs in a POST body.

Encoded URLs are unreadable to people. A screen reader announces a percent-encoded address as a string of letters and numbers, and so does a human reading it aloud. Use descriptive link text rather than pasting the bare URL, and keep a decoded copy in any documentation a person has to read.

Frequently asked questions

What is URL encoding, also called percent-encoding?

RFC 3986 allows only a small set of ASCII characters in a URL: letters, digits and a handful of marks. Anything else has to be written as one or more percent escapes. The text is first turned into bytes using UTF-8, then each byte becomes a percent sign and two hexadecimal digits. A space becomes %20, an e-acute becomes %C3%A9 because it is two bytes in UTF-8, and a maple leaf emoji becomes %F0%9F%8D%81 because it is four.

When should I use encodeURI and when encodeURIComponent?

Use encodeURI when you have a whole address and want to keep it working as an address. It leaves the structural characters alone, so the colon, the slashes, the question mark, the ampersand, the equals sign and the hash still separate the parts of the URL. Use encodeURIComponent when the string is a single value going into one query parameter or one path segment, because it also escapes those structural characters and stops a stray ampersand from splitting your value in two. This page offers both: the Whole URL scope runs encodeURI and decodeURI, the Single value scope runs encodeURIComponent and decodeURIComponent.

Why is a space sometimes %20 and sometimes a plus sign?

They come from two different rules. RFC 3986 percent-encoding, used in paths and in hand-built query strings, writes a space as %20. The older application/x-www-form-urlencoded format, used by HTML form submissions and produced by URLSearchParams, writes a space as a plus sign. Both are common and most servers accept either, but the decoders differ: decodeURIComponent turns %20 back into a space and leaves a plus sign as a literal plus.

Why does my decoded text come out as unreadable symbols?

Percent-encoding records bytes, not characters, and says nothing about which character set those bytes belong to. This page decodes them as UTF-8, which is what browsers and modern servers produce. If the escapes were made by an older system using Shift_JIS, GBK, Big5 or Windows-1252, decoding them as UTF-8 gives mojibake or fails outright. In that case you need a decoder that lets you name the original encoding.

What does the error "URI malformed" mean?

It means the string is not valid percent-encoding, so the decoder cannot finish. The usual causes are a lone percent sign that was meant literally and should have been written %25, a truncated escape such as %E6%A5 where the last byte was cut off, and a percent followed by something other than two hexadecimal digits. Encode the literal percent signs first, or check that the string was not shortened when it was copied out of a log.

What is double encoding and how do I spot it?

Double encoding happens when an already encoded string is encoded again. The percent sign of each escape is itself escaped to %25, so %20 becomes %2520 and %C3%A9 becomes %25C3%25A9. The giveaway is a run of %25 sequences, or text that still looks encoded after one decode pass. Decode it twice to recover the original, then fix the code that encoded it, usually a value that was encoded once when it was stored and again when the URL was built.

Do I need to encode a link before turning it into a QR code?

Yes, if the link contains spaces or non-ASCII text. A QR code carries the characters you give it, and the scanning app hands them to the browser as they are, so a raw space can truncate the link or send it to a search engine instead. Encode the address first, check that it still opens when you paste it into a browser, then generate the code. Shorter payloads also produce a simpler pattern that scans from further away.

Is there a maximum URL length?

The URL specification sets no limit, but every browser, server and proxy in the path sets its own, and the smallest one wins. Common web server defaults for the whole request line sit in the region of 8 KB, and some application servers and logging pipelines cut in earlier. Keeping a query string under about 2 KB is a safe rule of thumb. If you need to send more than that, use a POST body instead of a query string.

Does anything I paste here get uploaded?

No. The encoding and decoding run in your browser with the built-in encodeURI, encodeURIComponent, decodeURI and decodeURIComponent functions. Nothing you type is sent to a server, stored or logged, and the input and the result are masked so they do not appear in analytics session recordings. You can load the page once and keep using it with the network disconnected.

Which characters never need to be encoded?

The unreserved set from RFC 3986 is always safe: A to Z, a to z, 0 to 9, and the four marks hyphen, period, underscore and tilde. The reserved characters are safe only where they are doing their structural job. A slash separating path segments or an ampersand separating parameters must stay literal, while the same character inside a value must be escaped, which is exactly the distinction between encodeURI and encodeURIComponent.

Sources

More developer tools

Once a parameter is decoded it is often something else in disguise. Base64 payloads go to the Base64 encoder and decoder, an API response goes to the JSON formatter, and a bearer token in a query string can be opened with the JWT decoder to check its expiry. If you are generating identifiers to put in those URLs, the UUID generator produces v4 and v7 values that are already URL safe.

Related tools

Privacy: encoding and decoding happen in your browser with the built-in encodeURI, encodeURIComponent, decodeURI and decodeURIComponent functions. Nothing you paste is uploaded, stored or logged, and the input and result are masked from analytics session recordings. Analytics records only that the page was viewed.