Back to Blog
Technicalurlencodingweb

URL Encoding Guide: Understanding Percent-Encoding for the Web

Complete guide to URL encoding. Learn encodeURIComponent vs encodeURI, handle special characters, avoid common mistakes, and work with URLs in any language.

Loopaloo TeamSeptember 28, 202514 min read

Ever wondered why URLs sometimes contain strange sequences like %20 or %3D? That's URL encoding (also called percent-encoding), and understanding it is essential for working with web applications, APIs, and any system that uses URLs.

What is URL Encoding?

URL encoding converts characters into a format that can be safely transmitted in URLs. Since URLs have a restricted character set, special characters are replaced with a percent sign (%) followed by their two-digit hexadecimal value.

Space      →  %20
!          →  %21
#          →  %23
&          →  %26
=          →  %3D
?          →  %3F

Example:

Original:  Hello World! How are you?
Encoded:   Hello%20World%21%20How%20are%20you%3F

Why URL Encoding Exists

URLs were designed with a limited character set. Certain characters have special meanings in URLs:

https://example.com/path/to/page?name=John&age=30#section
  │         │          │          │         │      │
  │         │          │          │         │      └─ # starts fragment
  │         │          │          │         └─ & separates parameters
  │         │          │          └─ = separates key from value
  │         │          └─ ? starts query string
  │         └─ : separates scheme from host
  └─ / separates path segments

If your data contains these characters, they must be encoded to avoid breaking the URL structure.

Without encoding:
/search?q=rock & roll   ← Browser thinks "&" starts a new parameter

With encoding:
/search?q=rock%20%26%20roll   ← Correctly sends "rock & roll" as the value

Character Categories

Unreserved Characters (Never Encoded)

These characters are safe to use anywhere in a URL:

  • Letters: A-Z, a-z
  • Digits: 0-9
  • Special: - _ . ~
Hello-World_2024.txt   ← No encoding needed

Reserved Characters (Encoded When Used as Data)

These characters have special meanings in URLs. They must be encoded when used as data rather than delimiters:

CharacterMeaningEncoded
:Scheme separator (http:)%3A
/Path separator%2F
?Query string start%3F
#Fragment start%23
[IPv6 delimiter%5B
]IPv6 delimiter%5D
@User info separator%40
!Sub-delimiter%21
$Sub-delimiter%24
&Parameter separator%26
'Sub-delimiter%27
(Sub-delimiter%28
)Sub-delimiter%29
*Sub-delimiter%2A
+Space (in forms)%2B
,Sub-delimiter%2C
;Parameter separator%3B
=Key-value separator%3D

Unsafe Characters (Always Encoded)

These characters are not allowed in URLs and must always be encoded:

CharacterEncodedNotes
Space%20Also + in query strings
"%22Double quote
<%3CLess than
>%3EGreater than
\%5CBackslash
^%5ECaret
```%60Backtick
{%7BLeft brace
``%7C
}%7DRight brace

Non-ASCII Characters

Characters outside the ASCII range (like é, 中, 🎉) are encoded as their UTF-8 bytes:

café   →  caf%C3%A9
中文    →  %E4%B8%AD%E6%96%87
🎉     →  %F0%9F%8E%89

JavaScript Encoding Functions

encodeURIComponent()

Encodes a URI component (query parameter values, path segments). Encodes all characters except: A-Z a-z 0-9 - _ . ! ~ * ' ( )

encodeURIComponent('hello world')
// "hello%20world"

encodeURIComponent('a=1&b=2')
// "a%3D1%26b%3D2"

encodeURIComponent('rock & roll')
// "rock%20%26%20roll"

encodeURIComponent('https://example.com')
// "https%3A%2F%2Fexample.com"  ← Encodes : and /

Use when: Building query string values, path segments, or any part that should be treated as data.

encodeURI()

Encodes a complete URI. Preserves URL structure by NOT encoding: ; / ? : @ & = + $ , #

encodeURI('https://example.com/path?q=hello world')
// "https://example.com/path?q=hello%20world"

encodeURI('https://example.com/path?q=rock & roll')
// "https://example.com/path?q=rock%20&%20roll"
// Note: & is NOT encoded - this breaks the URL!

Use when: You have a complete URL that just needs non-ASCII or space characters encoded.

Warning: encodeURI() is rarely what you want. It doesn't encode & and =, which can break query strings.

decodeURIComponent() and decodeURI()

Reverse the encoding:

decodeURIComponent('hello%20world')
// "hello world"

decodeURIComponent('rock%20%26%20roll')
// "rock & roll"

decodeURI('https://example.com/path?q=hello%20world')
// "https://example.com/path?q=hello world"

URLSearchParams (Recommended)

The modern way to handle query strings - encoding is automatic:

// Building query strings
const params = new URLSearchParams();
params.set('name', 'John Doe');
params.set('query', 'rock & roll');
params.set('filter', 'price<100');

params.toString()
// "name=John+Doe&query=rock+%26+roll&filter=price%3C100"

// Parsing query strings
const parsed = new URLSearchParams('name=John+Doe&age=30');
parsed.get('name')  // "John Doe"
parsed.get('age')   // "30"

Note: URLSearchParams uses + for spaces (form encoding), not %20.

URL Constructor

For building complete URLs safely:

const url = new URL('https://example.com/search');
url.searchParams.set('q', 'hello world');
url.searchParams.set('filter', 'a=1&b=2');

url.toString()
// "https://example.com/search?q=hello+world&filter=a%3D1%26b%3D2"

url.href
// Same as toString()

Other Languages

Python

from urllib.parse import quote, unquote, urlencode

# Encode a string
quote('hello world')      # 'hello%20world'
quote('rock & roll')      # 'rock%20%26%20roll'

# Decode
unquote('hello%20world')  # 'hello world'

# Build query string (recommended)
urlencode({'name': 'John Doe', 'query': 'rock & roll'})
# 'name=John+Doe&query=rock+%26+roll'

PHP

// Encode
urlencode('hello world');       // "hello+world"
rawurlencode('hello world');    // "hello%20world"

// Decode
urldecode('hello%20world');     // "hello world"
rawurldecode('hello+world');    // "hello+world" (doesn't decode +)

// Build query string
http_build_query(['name' => 'John Doe', 'query' => 'rock & roll']);
// "name=John+Doe&query=rock+%26+roll"

Java

import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

// Encode
URLEncoder.encode("hello world", StandardCharsets.UTF_8);
// "hello+world"

// Decode
URLDecoder.decode("hello%20world", StandardCharsets.UTF_8);
// "hello world"

Go

import "net/url"

// Encode
url.QueryEscape("hello world")  // "hello+world"
url.PathEscape("hello world")   // "hello%20world"

// Decode
url.QueryUnescape("hello+world")  // "hello world"

// Build URL with query
u, _ := url.Parse("https://example.com/search")
q := u.Query()
q.Set("name", "John Doe")
u.RawQuery = q.Encode()
// https://example.com/search?name=John+Doe

Space Encoding: %20 vs +

Two encodings exist for spaces:

EncodingCharacterWhen Used
%20SpaceURLs in general
+Spaceapplication/x-www-form-urlencoded (forms, query strings)
// encodeURIComponent uses %20
encodeURIComponent('hello world')  // "hello%20world"

// URLSearchParams uses +
new URLSearchParams({q: 'hello world'}).toString()  // "q=hello+world"

Both are valid in query strings. Most servers accept either.

Common Mistakes

1. Double Encoding

Encoding an already-encoded string:

// First encoding
const encoded = encodeURIComponent('hello world');
// "hello%20world"

// Double encoding (WRONG!)
encodeURIComponent(encoded);
// "hello%2520world"  ← %25 is the encoding of %!

// When decoded once, you get:
// "hello%20world" instead of "hello world"

Solution: Only encode raw data once, right before building the URL.

2. Using encodeURI() for Query Values

const search = 'rock & roll';

// WRONG: encodeURI doesn't encode &
const url = 'https://example.com/search?q=' + encodeURI(search);
// "https://example.com/search?q=rock%20&%20roll"
// Browser sees: q=rock  AND  roll= (broken!)

// RIGHT: encodeURIComponent encodes &
const url = 'https://example.com/search?q=' + encodeURIComponent(search);
// "https://example.com/search?q=rock%20%26%20roll"

3. Forgetting to Encode User Input

// User enters: ../../etc/passwd
const filename = userInput;

// WRONG: Path traversal attack!
fetch('/files/' + filename);

// RIGHT: Encode the path segment
fetch('/files/' + encodeURIComponent(filename));

4. Not Decoding When Needed

When you receive encoded data (e.g., from a query string), decode it before using:

// URL: /search?q=hello%20world
const urlParams = new URLSearchParams(window.location.search);
const query = urlParams.get('q');  // Automatically decoded: "hello world"

// If manually parsing:
const raw = 'hello%20world';
const decoded = decodeURIComponent(raw);  // "hello world"

5. Encoding Entire URLs

const url = 'https://example.com/path?q=test';

// WRONG: Breaks the URL structure
encodeURIComponent(url);
// "https%3A%2F%2Fexample.com%2Fpath%3Fq%3Dtest"

// RIGHT: Only encode the parts that need it
const base = 'https://example.com/path';
const query = encodeURIComponent('hello world');
const fullUrl = base + '?q=' + query;

Security Considerations

Prevent Injection Attacks

Always encode user input before inserting into URLs:

// User input: javascript:alert('XSS')
const url = 'redirect?url=' + encodeURIComponent(userInput);
// Safe: "redirect?url=javascript%3Aalert%28%27XSS%27%29"

Validate After Decoding

Decode data before validation, not after:

// User submits: %2e%2e%2f (encoded ../)
const path = decodeURIComponent(userInput);  // "../"

// Now validate the decoded value
if (path.includes('..')) {
  throw new Error('Invalid path');
}

Complete Encoding Reference

CharacterURL EncodedHTML Entity
(space)%20 or + 
!%21-
"%22"
#%23-
$%24-
%%25-
&%26&
'%27-
(%28-
)%29-
*%2A-
+%2B-
,%2C-
/%2F-
:%3A-
;%3B-
<%3C<
=%3D-
>%3E>
?%3F-
@%40-
[%5B-
\%5C-
]%5D-
^%5E-
`%60-
{%7B-
%7C
}%7D-
~%7E-

Best Practices

  1. Use URLSearchParams for query strings - it handles encoding automatically
  2. Use encodeURIComponent() for individual values (path segments, query values)
  3. Never use encodeURI() for query string values
  4. Encode once, right before building the URL
  5. Decode data before validating it
  6. Use the URL constructor for building complete URLs safely
// Best practice: Use URL and URLSearchParams
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'rock & roll');
url.searchParams.set('page', '1');
console.log(url.toString());
// "https://example.com/search?q=rock+%26+roll&page=1"

Use our URL Encoder/Decoder tool to quickly encode or decode URLs and see the transformations in real-time.

Related Tools

Related Articles

Try Our Free Tools

200+ browser-based tools for developers and creators. No uploads, complete privacy.

Explore All Tools