Originally published on DevToolsHub β free, privacy-first online tools for developers.
Every developer has seen it: %20, %3A, %2F β URL encoding is everywhere. Whether you're building REST APIs, handling form submissions, parsing query strings, or debugging OAuth redirects, understanding URL encoding is essential.
π§ Try it yourself: Use the free URL Encoder / Decoder β supports both
encodeURIComponentandencodeURImodes with instant bidirectional conversion. All processing happens in your browser β zero data leaves your machine.
What is URL Encoding?
URL encoding (also called percent-encoding) converts special characters into a format that can be safely transmitted in URLs. Only a limited set of characters are allowed unescaped in URLs: alphanumeric characters (A-Z, a-z, 0-9) and a few special characters like -, _, ., ~.
Everything else gets encoded as a % followed by two hexadecimal digits representing the character's UTF-8 byte value.
| Character | Encoded | Why It's Encoded |
|---|---|---|
| Space | %20 |
Not allowed in URLs |
? |
%3F |
Starts query string |
& |
%26 |
Separates query params |
= |
%3D |
Key-value separator |
# |
%23 |
Starts fragment identifier |
/ |
%2F |
Path separator |
@ |
%40 |
Mailto/auth separator |
δΈ |
%E4%B8%AD |
Non-ASCII character |
The Two JavaScript Encoding Functions
JavaScript provides two built-in URL encoding functions β and choosing the wrong one is one of the most common bugs in web development.
encodeURI() β For Full URLs
encodeURI("https://example.com/search?q=hello world&lang=en")
// Result: "https://example.com/search?q=hello%20world&lang=en"
encodeURI() encodes special characters except those that have structural meaning in a URL (?, &, =, #, /). It assumes you're encoding a complete URL and preserves its structure.
Use when: you have a complete URL that contains characters needing encoding (like spaces in the path).
encodeURIComponent() β For Query Parameters
encodeURIComponent("hello world & more")
// Result: "hello%20world%20%26%20more"
encodeURIComponent("name=John & age=30")
// Result: "name%3DJohn%20%26%20age%3D30"
encodeURIComponent() encodes everything β including ?, &, =, #, and /. This is what you want when building query strings.
Use when: building query parameters, encoding user input for URLs, or handling data that needs to be embedded in a URL.
π΄ The Classic Bug
// β WRONG β using encodeURI for query params
const name = "John & Jane";
const url = `https://api.example.com?name=${encodeURI(name)}`;
// Result: "https://api.example.com?name=John%20&%20Jane"
// The "&" was NOT encoded! β BROKEN QUERY
// β
CORRECT β using encodeURIComponent for query params
const url = `https://api.example.com?name=${encodeURIComponent(name)}`;
// Result: "https://api.example.com?name=John%20%26%20Jane"
// Everything is encoded β
The Golden Rule: When building URL query strings, always use encodeURIComponent() for each parameter value. Never pass raw user input into a URL.
The Complete Character Reference
Here's what the two encoding modes leave untouched:
| Character Group | encodeURI() |
encodeURIComponent() |
|---|---|---|
A-Z, a-z, 0-9
|
β Preserved | β Preserved |
- _ . ~ |
β Preserved | β Preserved |
! ' ( ) * |
β Preserved | β Preserved |
? |
β Preserved | β Encoded β %3F
|
& |
β Preserved | β Encoded β %26
|
= |
β Preserved | β Encoded β %3D
|
# |
β Preserved | β Encoded β %23
|
/ |
β Preserved | β Encoded β %2F
|
| Space | β Encoded β %20
|
β Encoded β %20
|
@ |
β Encoded β %40
|
β Encoded β %40
|
: |
β Encoded β %3A
|
β Encoded β %3A
|
Real-World Scenarios
1. Building a Search URL
const baseUrl = "https://api.books.com/search";
const query = "JavaScript & CSS";
const category = "web dev";
const page = 1;
// β
Correct approach
const params = new URLSearchParams({
q: query,
cat: category,
page: page.toString()
});
const url = `${baseUrl}?${params.toString()}`;
// "https://api.books.com/search?q=JavaScript+%26+CSS&cat=web+dev&page=1"
Pro tip: Use URLSearchParams instead of manual string concatenation β it handles encoding correctly for all browsers.
2. Decoding URLs from Server Logs
Raw log entry:
GET /search?q=JavaScript+%26+CSS&referrer=https%3A%2F%2Fgoogle.com
After decoding:
Path: /search
q: JavaScript & CSS
referrer: https://google.com
3. Handling Unicode and Emoji
encodeURIComponent("δ½ ε₯½δΈη")
// Result: "%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C"
encodeURIComponent("π₯ π")
// Result: "%F0%9F%94%A5%20%F0%9F%9A%80"
// β
Decoding is symmetrical
decodeURIComponent("%E4%BD%A0%E5%A5%BD")
// Result: "δ½ ε₯½"
4. OAuth Redirect URLs
OAuth flows often require encoding the redirect_uri parameter:
const redirectUri = "https://myapp.com/callback?source=email&plan=premium";
const encoded = encodeURIComponent(redirectUri);
// "https%3A%2F%2Fmyapp.com%2Fcallback%3Fsource%3Demail%26plan%3Dpremium"
const oauthUrl = `https://auth.provider.com/authorize?redirect_uri=${encoded}&client_id=abc123`;
Common Pitfalls
Pitfall 1: Double Encoding
const input = "hello%20world";
// π΄ Double encoding β the %25 will be treated as a literal percent
const bad = encodeURIComponent(input);
// Result: "hello%2520world"
// ^^ This is "%25" followed by "20", NOT a space!
// β
Correct β only encode once
const good = input; // Already encoded, or use decodeURIComponent first
Pitfall 2: Forgetting to Decode on the Backend
Most web frameworks handle URL decoding automatically (Express, Flask, Spring Boot all do). But if you're building a custom URL parser, remember to call decodeURIComponent().
Pitfall 3: The + vs %20 Confusion
Application/x-www-form-urlencoded forms encode spaces as +. But URL encoding (percent-encoding) uses %20. JavaScript's encodeURIComponent() uses %20, not +. URLSearchParams uses +. Both work β just be consistent.
Language Quick Reference
| Language | Encode | Decode |
|---|---|---|
| JavaScript | encodeURIComponent(str) |
decodeURIComponent(str) |
| Python | urllib.parse.quote(str) |
urllib.parse.unquote(str) |
| Python (query) | urllib.parse.urlencode(dict) |
urllib.parse.parse_qs(str) |
| Java | URLEncoder.encode(str, "UTF-8") |
URLDecoder.decode(str, "UTF-8") |
| Go | url.QueryEscape(str) |
url.QueryUnescape(str) |
| PHP | urlencode($str) |
urldecode($str) |
| Ruby | ERB::Util.url_encode(str) |
ERB::Util.url_decode(str) |
| C# | Uri.EscapeDataString(str) |
Uri.UnescapeDataString(str) |
| Rust | urlencoding::encode(str) |
urlencoding::decode(str) |
URL Encoding Cheat Sheet
Quick Decision Tree
You have a complete URL? β use encodeURI()
You have a query param value? β use encodeURIComponent()
You're debugging encoded data? β use decodeURIComponent()
Building query strings? β use URLSearchParams() β
Characters That Must Always Be Encoded in Query Params
<space> β %20 # hash β %23
& β %26 % percent β %25
? β %3F / slash β %2F
= β %3D @ at β %40
TL;DR β Just Remember This
-
encodeURIComponent()for query parameter values (encodes everything) -
encodeURI()only if you need to encode a complete URL string -
Use
URLSearchParamsβ it handles both encoding and parameter formatting - All encoding/decoding is local β no server processing required
π Need a quick encode/decode? Try the URL Encoder / Decoder tool β built in the browser, safe with your sensitive data, supports both encoding modes. Also check out JSON Formatter and JWT Decoder for more handy dev tools.
Found this helpful? Follow me for more developer guides and practical web development tips. If you spot an error or have a suggestion, drop a comment below!
Top comments (0)