How to Encode and Decode URLs
If you have ever seen %20 in a URL where a space should be, or %C3%A9 where an accented character belongs, you have encountered URL encoding. It is a fundamental part of how the web works, and understanding it helps you debug broken links, API issues, and form submissions. A browser-based encoder handles the entire job locally without uploading your data to a server.
What URL encoding does
URLs can only contain a limited set of characters safely: letters (A-Z, a-z), digits (0-9), and a few special characters (-, _, ., ~). Everything else (spaces, accented characters, emoji, and symbols like &, =, #, ?) must be converted to a safe format.
URL encoding (also called percent-encoding) replaces unsafe characters with a % followed by their hexadecimal byte values:
| Character | Encoded |
|---|---|
| Space | %20 |
| & | %26 |
| = | %3D |
| # | %23 |
| ? | %3F |
| / | %2F |
| @ | %40 |
| : | %3A |
| + | %2B |
| , | %2C |
| ; | %3B |
| (newline) | %0A |
| (tab) | %09 |
When you need URL encoding
- Query parameters with special characters: a search query like
price > 100 & category = shoesneeds encoding to work in a URL - Non-English characters in URLs: names, cities, or content in other languages must be encoded
- API requests: when building API calls manually, parameter values often need encoding
- Debugging: when a URL is not working, decoding it reveals what the actual values are
- Email links (mailto:): subject lines and body text in mailto links need encoding
- OAuth redirect URIs: the redirect_uri parameter passed to OAuth providers must be fully encoded
- Webhook payloads: query strings in webhook URLs delivered by services like Stripe or Slack
- Deep links into mobile apps: custom URL schemes for iOS/Android apps need encoding for safe handling
- GraphQL persisted queries: hashed queries appended as URL parameters need encoding
- PostgreSQL connection strings: passwords and other special characters in DATABASE_URL values
How to encode and decode
- Choose encode or decode: select the direction. Pick encodeURIComponent for query parameters or encodeURI for full URLs.
- Paste your input: enter the text or URL. The result updates instantly.
- Copy the output: use the result in your code, API request, or browser.
A brief history of URL encoding
URL encoding was defined by RFC 1738 in December 1994, alongside the original URL specification. The RFC was written by Tim Berners-Lee (inventor of the web) with input from the IETF URI Working Group. The original encoding scheme used ASCII byte values: every reserved or unsafe character was encoded as % followed by two hex digits.
The encoding was updated several times:
- RFC 1738 (1994): original URL specification, ASCII-only
- RFC 2396 (1998): more rigorous syntax, separated "reserved" from "unreserved" characters
- RFC 3986 (2005): current URI specification, defines two encoding modes (path vs query), UTF-8 byte sequences for non-ASCII
- WHATWG URL Standard (ongoing): the browser-standard living spec, used by all modern browsers, slightly different rules than RFC 3986 for backward compatibility
The biggest change was the move to UTF-8 in RFC 3986. Before that, encoded URLs were ASCII-only, and non-Latin characters required workarounds (Punycode for domains, IDN for international addresses). Today, an accented "é" in a URL encodes to %C3%A9 (its two UTF-8 bytes), not the Latin-1 byte %E9 that older systems would produce.
encodeURI vs encodeURIComponent vs encodeURIFull
JavaScript has three encoding functions with subtly different behavior:
| Function | What it encodes | What it preserves | Use for |
|---|---|---|---|
| encodeURI() | All unsafe chars | URL syntax: : / ? & = # | Encoding whole URLs |
| encodeURIComponent() | All unsafe chars including URL syntax | Only A-Z a-z 0-9 - _ . ~ ! * ' ( ) | Query parameter values |
| escape() (deprecated) | Most unsafe chars | Latin-1 only | Do not use |
In Python:
urllib.parse.quote()is like encodeURI (preserves /, but not :)urllib.parse.quote_plus()is like encodeURIComponent but uses + for spacesurllib.parse.urlencode(dict)encodes whole query strings
In other languages:
| Language | Component encoding | Full URI encoding |
|---|---|---|
| Java | URLEncoder.encode() (with caveats around +) | URI.toASCIIString() |
| C# | Uri.EscapeDataString | Uri.EscapeUriString |
| Ruby | CGI.escape() | URI.encode_www_form_component |
| PHP | rawurlencode() | urlencode() (note: %2B vs +) |
| Go | url.QueryEscape() | url.PathEscape() |
| Rust | percent_encoding crate | percent_encoding crate |
Common pitfalls
- Encoding the whole URL: if you encode
https://example.com/search?q=hello, you gethttps%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhellowhich is no longer a working URL. Only encode the values, not the structural characters. - Double encoding: encoding an already-encoded string produces things like
%2520(the%gets encoded as%25). If your URL looks wrong, check if something is being encoded twice. - Spaces as + vs %20: in
application/x-www-form-urlencoded(form POST bodies), spaces become+. In URLs, spaces become%20. Most servers accept both, but some strict parsers do not. - Encoding the reserved characters wrongly:
?,#,&,=have special meaning in URL syntax. If they appear in a value, they must be encoded; if they appear as syntax, they must not be. - Forgetting to decode on receive: if you encode a value, send it, then your server reads
?q=hello%20worldliterally without decoding, your application seeshello%20worldnothello world. Most frameworks decode automatically, but verify in custom code. - Plus sign confusion:
+is a literal plus in path segments and a space in query strings. Encode actual plus signs as%2Bin query values to avoid ambiguity. - UTF-8 vs other encodings: if your URL contains "résumé" and the server expects Latin-1 instead of UTF-8, you may get mojibake. Modern web is UTF-8; legacy systems are not.
- URL length limits: even though there is no strict limit in the spec, browsers and servers often cap URLs at 2048-8192 characters. Heavily-encoded data can hit limits faster than expected.
- Cookies and Referer headers: URLs are passed in Referer headers and may be logged. Sensitive data in URLs (passwords, tokens) leaks to logs and analytics. Use POST bodies for sensitive data.
- Non-ASCII domain names: domains use Punycode (RFC 3492), not percent encoding. "münchen.de" becomes "xn--mnchen-3ya.de" in DNS lookups, not "m%C3%BCnchen.de".
Worked examples
| Input | encodeURI | encodeURIComponent |
|---|---|---|
hello world |
hello%20world |
hello%20world |
q=test&page=1 |
q=test&page=1 |
q%3Dtest%26page%3D1 |
https://x.com/path |
https://x.com/path |
https%3A%2F%2Fx.com%2Fpath |
caf é |
caf%20%C3%A9 |
caf%20%C3%A9 |
中文 |
%E4%B8%AD%E6%96%87 |
%E4%B8%AD%E6%96%87 |
100% |
100%25 |
100%25 |
email@test.com |
email@test.com |
email%40test.com |
Tips
- Encode values, not entire URLs: if you encode an entire URL, the slashes and colons that structure the URL will also be encoded, breaking it. Only encode the values within query parameters.
- Double encoding: encoding an already-encoded string produces things like
%2520(the%gets encoded as%25). If your URL looks wrong, check if something is being encoded twice. - Decode for debugging: when an API request fails or a URL looks garbled, decode it to see the actual parameter values. This often reveals the problem immediately.
- Use your language's built-in functions: in production code, always use
encodeURIComponent()(JavaScript),urllib.parse.quote()(Python), orURLEncoder.encode()(Java) rather than encoding by hand. - Test with edge cases: try inputs with spaces, accents, emoji, and special characters. If your encoding works for all of them, it works.
- Verify in browser address bar: paste your encoded URL into a browser. If the page loads, the URL is well-formed. If not, your encoding has a bug.
- Use a query-string library for complex cases: building a query string from a dictionary or object (
?a=1&b=2&c=3) is easier and safer with a library function (urlencode in Python, URLSearchParams in JavaScript) than manual assembly. - Know the difference between path and query encoding: a forward slash
/in a path segment is structural; in a query value, it must be encoded. RFC 3986 has different rules for each.
Privacy and confidential URLs
The URL encoder and decoder run entirely in your browser. The URLs you paste, intermediate processing, and the encoded/decoded output all stay on your device. Nothing is uploaded to a server, logged, or shared with anyone.
This matters because URLs frequently contain extremely sensitive data: API keys and tokens in query parameters, OAuth authorization codes that grant account access, session IDs, signed URLs for private S3 buckets with embedded credentials, magic-link login tokens, password reset URLs, internal admin URLs that reveal product structure, customer email addresses in unsubscribe links, personal data in form submissions. Cloud URL encoders log every paste, sometimes retain them for "service improvement," and have been involved in real leaks where pasted authentication tokens were extracted by attackers monitoring the logs. A browser-based encoder has zero exposure: the URL never leaves your machine.
Browser-based encoding also works offline once the page is loaded, useful for encoding URLs on airplanes, in secure environments without internet access, or anywhere you cannot or should not paste authentication-bearing URLs into a third-party service.
Frequently Asked Questions
What is the difference between encodeURI and encodeURIComponent?
encodeURI preserves characters that are valid in a URL structure (slashes, colons, question marks). encodeURIComponent encodes everything except letters, digits, and a few safe characters. Use encodeURIComponent for query parameter values, encodeURI for complete URLs.
Why do spaces become %20 or +?
In URL encoding, spaces become %20. In form data (application/x-www-form-urlencoded), spaces become +. Both are valid in their respective contexts, but %20 is the universal standard for URLs.
Do I need to encode my URLs manually?
In most cases, your programming language or framework handles encoding automatically. Manual encoding is needed when building URLs by hand, debugging API requests, or working with query strings that contain special characters.
Is my data sent to a server?
No. All encoding and decoding happens in your browser.