How to Encode and Decode URLs: Percent-Encoding Explained
By Akmal Ali · Published · Updated · 5 min read
URL encoding replaces characters that aren’t allowed or have a special meaning in a URL with % and two hexadecimal digits per byte, so a space becomes %20 and & becomes %26. Encode each value you put into a URL, such as a search term, but not the whole address. Decoding reverses it.
A search link for “fish & chips” comes back with results for “fish”. An email link shows caf%C3%A9 where you expected “café”. A redirect URL gets mangled somewhere between two servers. All three come down to URL encoding, and to encoding the right part of the address.
Why do URLs need encoding?
A URL can only contain a limited set of characters, and some of those have jobs. / separates path segments, ? starts the query string, & separates parameters, = joins a name to its value and # starts a fragment. When a value contains one of these, or a space or a character outside ASCII, it has to be written in a form that can’t be confused with the URL’s structure.
Without encoding, ?q=fish & chips is read as a parameter q=fish followed by a second, empty parameter called chips.
How does percent-encoding work?
Each character to encode is turned into its bytes in UTF-8, and each byte is written as % followed by two hexadecimal digits. This is defined in RFC 3986.
- A space is one byte, 0x20, so it becomes
%20. - é is two bytes in UTF-8, so
cafébecomescaf%C3%A9. - 👋 is four bytes, so it becomes
%F0%9F%91%8B.
Letters A–Z and a–z, digits and the four characters - . _ ~ never need encoding. Everything else is either reserved for structure or has to be encoded. The ones you’ll meet most:
| Character | Encoded | Why it matters |
|---|---|---|
| space | %20 | Not allowed in URLs |
& | %26 | Separates query parameters |
= | %3D | Joins a parameter’s name and value |
? | %3F | Starts the query string |
# | %23 | Starts the fragment; never sent to the server |
/ | %2F | Separates path segments |
+ | %2B | Often read as a space in query strings |
% | %25 | Starts every escape |
Encoding a value or a whole URL
This is the distinction that causes most bugs. There are two different jobs:
- Encoding a value that goes inside a URL, such as a search term, encodes the structural characters too.
fish & chipsbecomesfish%20%26%20chips, so the&can’t start a new parameter. In JavaScript, this isencodeURIComponent(). - Encoding a whole URL must leave
/ ? & = #alone, or the address stops working. Only spaces and characters that aren’t allowed are encoded. In JavaScript, this isencodeURI().
Encode each value separately, then assemble the URL: https://example.com/search?q=fish%20%26%20chips. Running encodeURI() on the finished string instead gives ?q=fish%20&%20chips, and the search term is split in two.
Using the encoder
In the URL encoder and decoder, choose “Part of a URL” for a value or path segment and “A whole URL” for a complete address, then paste your text. When decoding form data, turn on “Treat + as a space”. The tool also warns when text is already encoded, so it isn’t encoded twice, and points out broken escapes such as %ZZ.
URL Encoder / Decoder
Percent-encode a value or a whole URL as UTF-8, or decode one, with warnings for double encoding and broken escapes.
Spaces: %20 or +?
Both appear, for historical reasons. In a URL path, a space is always %20. HTML forms, and functions that build query strings the same way, write spaces in the query as +:
new URLSearchParams({ q: "fish & chips", lang: "en" }).toString()
// "q=fish+%26+chips&lang=en"Decoders for form data read + as a space; a plain URL decoder leaves it as a plus sign. That’s why a literal plus in a value must be written %2B.
URL encoding in code
- JavaScript:
encodeURIComponent()anddecodeURIComponent()for values;URLSearchParamsto build or read a query string. - Python:
urllib.parse.quote()keeps/by default;quote_plus()andurlencode()use+for spaces;unquote()decodes. - PHP:
rawurlencode()writes spaces as%20;urlencode()writes them as+.
Common mistakes
- Encoding twice. Encoding
hello%20worldagain giveshello%2520world, because%becomes%25. Encode raw values once, at the point where you build the URL. - Encoding a whole URL as a value.
https://example.com/a?b=1becomeshttps%3A%2F%2Fexample.com%2Fa%3Fb%3D1. That’s right when the URL is itself a parameter, such as a redirect target, and wrong when it’s the address you want to open. - Building query strings by joining text. Use URLSearchParams or your language’s equivalent, which encode each value for you.
- Decoding more than once. Decoding user input repeatedly can turn harmless text into characters your code didn’t expect. Decode exactly once.
- Mistaking encoding for security. Percent-encoding, like Base64, hides nothing. Anyone can decode it.
Related encodings
URL encoding makes text safe inside a URL. Base64 makes binary data safe as text, and has its own URL-safe variant; see how Base64 works. And if you control the URL, the simplest fix is to need less encoding in the first place: a lowercase, hyphenated slug from the slug generator needs none. The guide to SEO-friendly URL slugs explains how.
Frequently Asked Questions
Which characters don’t need encoding in a URL?
Letters A–Z and a–z, digits 0–9 and the four characters hyphen, full stop, underscore and tilde. Everything else is either reserved for the URL’s structure or must be encoded.
Why does my link break when I paste it into an email or chat?
Some apps end a link at the first space, quotation mark or bracket. Percent-encode those characters before you share the link, so the whole address stays one link.
How do I encode a URL in Excel or Google Sheets?
Both have an ENCODEURL function: =ENCODEURL(A1) percent-encodes the text in A1, much like encodeURIComponent. Neither has a built-in function to decode it again.
Is it safe to put an email address in a URL?
It works once the @ is encoded as %40, but anyone who sees the link can read the address, and it ends up in browser history and server logs. Keep personal data out of URLs where you can.