ToolPopToolPop
Back to BlogTutorials

URL Encoding and Decoding: Essential Guide for Web Developers

URLs can only contain certain characters. Learn how URL encoding works and when to apply it in your web applications.

ToolPop TeamMarch 18, 202512 min read

What is URL Encoding?

URL encoding, also known as percent encoding, converts characters into a format that can be safely transmitted in URLs. Characters that aren't allowed in URLs are replaced with a percent sign (%) followed by their hexadecimal ASCII value.

Why URL Encoding Exists

URLs can only contain a limited set of characters:

  • Letters: A-Z, a-z
  • Numbers: 0-9
  • Special: - _ . ~
  • Reserved: : / ? # [ ] @ ! $ & ' ( ) * + , ; =
All other characters must be encoded.

How It Works

Space     → %20
&         → %26
=         → %3D
?         → %3F
#         → %23
%         → %25
+         → %2B

Example:

Original: Hello World!
Encoded:  Hello%20World%21

Reserved vs. Unreserved Characters

Unreserved (Never need encoding)

A-Z a-z 0-9 - _ . ~

Reserved (Encode when used as data)

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

JavaScript URL Encoding Functions

encodeURIComponent()

Encodes everything except: A-Z a-z 0-9 - _ . ! ~ * ' ( )

// Use for query parameter values
const searchTerm = 'hello world & more';
const encoded = encodeURIComponent(searchTerm);
// Result: "hello%20world%20%26%20more"

const url = `https://example.com/search?q=${encoded}`;
// https://example.com/search?q=hello%20world%20%26%20more

encodeURI()

Encodes everything except: A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) #

// Use for entire URLs (preserves structure)
const url = 'https://example.com/path with spaces/page';
const encoded = encodeURI(url);
// Result: "https://example.com/path%20with%20spaces/page"

When to Use Which

ScenarioFunction
Query parameter valueencodeURIComponent()
Path segmentencodeURIComponent()
Entire URL (preserve structure)encodeURI()
Form dataencodeURIComponent()

Decoding

// Decode component
decodeURIComponent('hello%20world');
// Result: "hello world"

// Decode URI
decodeURI('https://example.com/path%20name');
// Result: "https://example.com/path name"

Building URLs Safely

Using URLSearchParams

// The modern way to build query strings
const params = new URLSearchParams();
params.append('name', 'John Doe');
params.append('query', 'hello & world');
params.append('special', '100% complete');

console.log(params.toString());
// "name=John+Doe&query=hello+%26+world&special=100%25+complete"

// Or initialize with object
const params2 = new URLSearchParams({
  name: 'John',
  age: '30'
});

Using URL API

const url = new URL('https://example.com/search');
url.searchParams.set('q', 'hello world');
url.searchParams.set('page', '1');

console.log(url.toString());
// "https://example.com/search?q=hello+world&page=1"

// Parsing existing URLs
const parsed = new URL('https://example.com/path?name=John%20Doe');
console.log(parsed.searchParams.get('name')); // "John Doe"

URL Encoding in Different Languages

Python

from urllib.parse import quote, quote_plus, unquote, urlencode

# Encode path component
quote('hello world')  # 'hello%20world'

# Encode for query string (space = +)
quote_plus('hello world')  # 'hello+world'

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

# Build query string
params = {'name': 'John Doe', 'age': '30'}
urlencode(params)  # 'name=John+Doe&age=30'

PHP

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

// Decode
$decoded = urldecode('hello+world');  // "hello world"
$decoded = rawurldecode('hello%20world');  // "hello world"

// Build query string
$params = ['name' => 'John', 'age' => 30];
$query = http_build_query($params);  // "name=John&age=30"

Java

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

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

// Decode
String decoded = URLDecoder.decode("hello+world", StandardCharsets.UTF_8);
// "hello world"

Go

import "net/url"

// Encode
encoded := url.QueryEscape("hello world")
// "hello+world"

// Decode
decoded, _ := url.QueryUnescape("hello+world")
// "hello world"

// Build URL with params
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

Common Encoding Scenarios

1. Search Queries

const searchTerm = 'C++ programming';
const url = `/search?q=${encodeURIComponent(searchTerm)}`;
// /search?q=C%2B%2B%20programming

2. File Names in URLs

const fileName = 'Report (Final).pdf';
const url = `/files/${encodeURIComponent(fileName)}`;
// /files/Report%20%28Final%29.pdf

3. Redirect URLs

const returnUrl = 'https://example.com/page?id=123';
const loginUrl = `/login?redirect=${encodeURIComponent(returnUrl)}`;
// /login?redirect=https%3A%2F%2Fexample.com%2Fpage%3Fid%3D123

4. JSON in Query Strings

const filters = JSON.stringify({ status: 'active', type: 'user' });
const url = `/api/items?filters=${encodeURIComponent(filters)}`;
// /api/items?filters=%7B%22status%22%3A%22active%22%2C%22type%22%3A%22user%22%7D

Space Encoding: %20 vs +

The handling of spaces differs:

ContextSpace becomes
Path%20
Query string (HTML forms)+
encodeURIComponent%20
URLSearchParams+
// Both are valid for query strings
'/search?q=hello%20world'  // Works
'/search?q=hello+world'    // Also works

// But in paths, only %20 is correct
'/files/my%20file.pdf'     // Correct
'/files/my+file.pdf'       // Looks for file named "my+file.pdf"

Double Encoding Issues

A common mistake is encoding already-encoded strings:

// Original
const url = 'https://example.com/search?q=hello%20world';

// ❌ Double encoding
encodeURI(url);
// "https://example.com/search?q=hello%2520world"
// %25 is the encoded form of %

// ✅ Check if already encoded
function safeEncode(str) {
  try {
    // If decoding changes nothing, it's not encoded
    return decodeURIComponent(str) === str
      ? encodeURIComponent(str)
      : str;
  } catch (e) {
    // Invalid encoding, encode it
    return encodeURIComponent(str);
  }
}

Handling Unicode

Modern URLs support Unicode through Punycode (for domains) and UTF-8 encoding:

// Unicode in query parameters
const query = '日本語';
encodeURIComponent(query);
// "%E6%97%A5%E6%9C%AC%E8%AA%9E"

// Each UTF-8 byte is percent-encoded
// 日 = E6 97 A5 (3 bytes) = %E6%97%A5

Security Considerations

1. Never Trust Encoded Input

// User could submit already-encoded malicious input
const userInput = '%3Cscript%3Ealert(1)%3C/script%3E';
const decoded = decodeURIComponent(userInput);
// "<script>alert(1)</script>"

// Always sanitize after decoding!

2. Validate URLs

function isValidUrl(string) {
  try {
    const url = new URL(string);
    return ['http:', 'https:'].includes(url.protocol);
  } catch (_) {
    return false;
  }
}

3. Prevent Open Redirects

// ❌ Dangerous - allows redirect to any site
const redirect = req.query.redirect;
res.redirect(redirect);

// ✅ Validate redirect destination
const allowedHosts = ['example.com', 'app.example.com'];
const url = new URL(redirect, 'https://example.com');
if (allowedHosts.includes(url.hostname)) {
  res.redirect(redirect);
}

Conclusion

URL encoding is fundamental to web development. Understanding when and how to encode URLs prevents bugs, security issues, and data corruption. Use the built-in APIs (URL, URLSearchParams) when possible, and always encode user input before including it in URLs.

Use our free URL Encoder/Decoder tool to quickly encode or decode URLs during development and debugging.

Tags
url encodeurl decodepercent encodingurlencodeencodeURIComponentquery string encoding
Share this article

Try Our Free Tools

Put these tips into practice with our free online tools. No signup required.

Explore Tools