URL Encoding & Decoding Guide: Complete Reference

Published: June 7, 2026 · 11 min read

Table of Contents

Why URL Encoding Matters

URLs were designed to transmit text using a limited character set (ASCII). When you need to include characters outside this set—or special characters that have meaning in URLs—you must encode them. This process, called percent-encoding or URL encoding, ensures URLs remain valid and properly interpreted.

Consider a search for "coffee & cream":

Unencoded:  https://example.com/search?q=coffee & cream
Problem:    The space breaks the URL; & is interpreted as parameter separator

Encoded:    https://example.com/search?q=coffee%20%26%20cream
Result:     Works correctly, server receives "coffee & cream"

Without encoding, URLs containing spaces, special characters, or non-ASCII text would be misinterpreted or rejected by servers and browsers.

When to Encode

How Percent-Encoding Works

Percent-encoding replaces each unsafe character with a % followed by two hexadecimal digits representing the character's ASCII value.

Encoding Process

1. Take character: " "
2. Find ASCII value: 32
3. Convert to hex: 20
4. Prefix with %: %20

Result: Space becomes %20

Common Encoded Characters

CharacterASCIIEncodedReason
Space32%20Breaks URL structure
!33%21Reserved in some contexts
#35%23Fragment delimiter
$36%24Reserved
&38%26Parameter separator
+43%2BEncoded as space in query strings
/47%2FPath separator
:58%3AProtocol separator
?63%3FQuery string delimiter
[91%5BReserved (IPv6)
]93%5DReserved (IPv6)

Safe Characters (Unencoded)

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

These characters don't need encoding in any URL component. The tilde (~) is technically safe but some older systems don't handle it well.

Reserved Characters Reference

RFC 3986 defines reserved characters that have special meaning in URLs:

reserved    = gen-delims / sub-delims
gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims  = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

Character Meanings

CharacterNameUse
:ColonProtocol (http:), port (:8080)
/SlashPath segments
?Question markQuery string start
#HashFragment identifier
[ ]BracketsIPv6 addresses
@At signUserinfo, email
&AmpersandParameter separator in query
=EqualsKey-value separator
+PlusHistorical: space in query strings
$DollarSubstitution syntax in some frameworks

JavaScript Encoding Functions

JavaScript provides several functions for URL encoding, and choosing the right one matters.

encodeURIComponent vs encodeURI

encodeURI()        // Encodes full URL, preserves safe characters
encodeURIComponent() // Encodes component, encodes more characters

encodeURI

Use for encoding complete URLs. Does NOT encode:

A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) #
const url = 'https://example.com/path/file.html';
const search = 'coffee & tea';

encodeURI(url)           // https://example.com/path/file.html
encodeURI(search)        // coffee%20&%20tea
// & is not encoded because it's valid in query strings

// Complete URL with query
encodeURI(url + '?q=' + search)
// https://example.com/path/file.html?q=coffee%20&%20tea

encodeURIComponent

Use for encoding individual parameter values. Does NOT encode:

A-Z a-z 0-9 - _ . ! ~ * ' ( )

const value = 'coffee & tea';

encodeURIComponent(value)    // coffee%20%26%20tea
// Spaces become %20, & becomes %26

// Safe for embedding in query strings
const url = 'https://example.com/search?q=' + encodeURIComponent(value);
// https://example.com/search?q=coffee%20%26%20tea

decodeURI vs decodeURIComponent

decodeURI()        // Decodes encodeURI() output
decodeURIComponent() // Decodes encodeURIComponent() output

decodeURIComponent('coffee%20%26%20tea')  // "coffee & tea"
decodeURIComponent('Hello%20World')        // "Hello World"

Modern URL API (Recommended)

// Modern approach using URLSearchParams
const params = new URLSearchParams();
params.append('q', 'coffee & tea');
params.append('category', 'beverages');

// Automatic encoding
console.log(params.toString());
// q=coffee%20%26%20tea&category=beverages

// Parse and handle encoded URLs
const parsed = new URL('https://example.com/search?q=coffee%20%26%20tea');
console.log(parsed.searchParams.get('q'));  // "coffee & tea"

// Update search params
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'espresso & latte');
console.log(url.href);
// https://example.com/search?q=espresso%20%26%20latte

Common Encoding Patterns

1. Search Query

Input:  "What is URL encoding?"
Output: "What%20is%20URL%20encoding%3F"

URL: https://search.example.com?q=What%20is%20URL%20encoding%3F

2. File Path with Spaces

Input:  "/My Documents/Quarterly Report.pdf"
Output: "/My%20Documents/Quarterly%20Report.pdf"

URL: https://docs.example.com/My%20Documents/Quarterly%20Report.pdf

3. Email Address in URL

Input:  "[email protected]"
Output: "user%40example.com"

URL: https://api.example.com/users?email=user%40example.com

4. Special Characters

Input:  "Price: $100 & 50¢"
Output: "Price%3A%20%24100%20%26%2050%C2%A2"

5. Multiple Parameters

// Build query string correctly
const params = new URLSearchParams();
params.append('name', 'John Doe');
params.append('city', 'New York, NY');
params.append('age', '30');

console.log(params.toString());
// name=John%20Doe&city=New%20York%2C%20NY&age=30

Query String Encoding

Historical: + for Spaces

Before modern standards, query strings used + for spaces instead of %20. This still works but is deprecated:

// Old style (still parsed correctly by most servers)
search=coffee+and+tea

// Modern style
search=coffee%20and%20tea

// Most parsers handle both
URLSearchParams handles this automatically

Building Query Strings

// Manual (error-prone)
const query = 'name=' + encodeURIComponent(name) + '&city=' + encodeURIComponent(city);

// Better: URLSearchParams
const params = new URLSearchParams({ name, city });
const query = params.toString();

// Multiple values
const params = new URLSearchParams();
params.append('tag', 'coffee');
params.append('tag', 'organic');
console.log(params.toString());
// tag=coffee&tag=organic

Parsing Query Strings

// URL-based parsing
const url = new URL('https://example.com/search?q=test&page=1');
const q = url.searchParams.get('q');      // "test"
const page = url.searchParams.get('page'); // "1"

// Using location
const searchParams = new URLSearchParams(window.location.search);

Internationalized URLs

Percent-Encoding Non-ASCII Characters

// Chinese characters
Input:  "北京"
UTF-8:  E5 8C 97 E4 BA AC
Encoded: "%E5%8C%97%E4%BA%AC"

URL: https://example.com/search?q=%E5%8C%97%E4%BA%AC

Punycode for Domain Names

Internationalized domain names use Punycode encoding:

Domain:  例え.テスト.com
Punycode: xn--r8jz45g.jp.xn--tckwe9d.com

Browser displays: 例え.テスト.com
URL bar shows:    xn--r8jz45g.jp.xn--tckwe9d.com

IDN Phishing Concerns

// Visually identical domains (homograph attack)
аррӏе.com (Cyrillic) vs apple.com (Latin)
раураӏ.com (Cyrillic) vs paypal.com (Latin)

// Modern browsers show Punycode in URL bar for suspicious IDs
// Only allow scripts from registered domains

Best Practices for i18n

// Always encode user input
const encoded = encodeURIComponent(userInput);

// URL-encode path segments
const path = '/search/' + encodeURIComponent(query);

// Handle Unicode properly
const emoji = '🔍';
encodeURIComponent(emoji);  // "%F0%9F%94%8D"

Implementation in Backend Languages

Python

import urllib.parse

# Encode
encoded = urllib.parse.quote('coffee & tea')
# 'coffee%20%26%20tea'

# Encode for query strings
params = urllib.parse.urlencode({'q': 'coffee & tea', 'page': 1})
# 'q=coffee+%26+tea&page=1'

# Decode
decoded = urllib.parse.unquote('coffee%20%26%20tea')
# 'coffee & tea'

# Parse query string
parsed = urllib.parse.parse_qs('q=coffee&type=hot')
# {'q': ['coffee'], 'type': ['hot']}

Node.js

const { encodeURIComponent, decodeURIComponent } = require('querystring');

// Basic encoding
const encoded = encodeURIComponent('coffee & tea');
// 'coffee%20%26%20tea'

// Query string module
const querystring = require('querystring');
const qs = querystring.stringify({ q: 'coffee & tea', page: 1 });
// 'q=coffee%20%26%20tea&page=1'

// Parse
const parsed = querystring.parse('q=coffee&page=1');
// { q: 'coffee', page: '1' }

Ruby

require 'cgi'

# Encode
encoded = CGI.escape('coffee & tea')
# 'coffee+%26+tea'

# HTML escape (for display)
escaped = CGI.escapeHTML('& < > " \' ')

# Decode
decoded = CGI.unescape('coffee%20%26%20tea')
# 'coffee & tea'

# URL encode
require 'uri'
encoded = URI.encode_www_form_component('coffee & tea')
# 'coffee%20%26%20tea'

PHP

// Encode
$encoded = urlencode('coffee & tea');
// 'coffee+26+tea' (query string style)

$encoded = rawurlencode('coffee & tea');
// 'coffee%20%26%20tea' (RFC 3986 style)

// Decode
$decoded = urldecode('coffee+26+tea');
$decoded = rawurldecode('coffee%20%26%20tea');

// Build query string
$params = http_build_query(['q' => 'coffee & tea', 'page' => 1]);
// 'q=coffee+%26+tea&page=1'

Security Considerations

Injection Attacks

Improper encoding can lead to security vulnerabilities:

// Dangerous: User input directly in URL
// User enters: "test"; window.location='evil.com'
// Result: https://site.com?q=test"; window.location='evil.com'

// Safe: Always encode user input
const safeUrl = 'https://site.com?q=' + encodeURIComponent(userInput);
// Result: https://site.com?q=test%22%3B%20window.location%3D%27evil.com%27

XSS Prevention

// When inserting URL parameters into HTML
// Use proper escaping for the context

// In HTML attribute
const html = '<a href="https://site.com?q=' + 
             encodeURIComponent(value).replace(/"/g, '%22') + 
             '">Link</a>';

// In JavaScript
const link = document.createElement('a');
link.href = 'https://site.com?q=' + encodeURIComponent(value);
link.textContent = 'Click me';

SSRF Prevention

// Never use user input directly in URLs without validation
// Validate and sanitize user input

const userUrl = req.query.url;
if (!isValidUrl(userUrl)) {
    return res.status(400).send('Invalid URL');
}
// Then encode when constructing the request
const encodedUrl = encodeURIComponent(userUrl);

Best Practices

Encode and decode URLs instantly with the JieBang URL Encoder/Decoder tool.

Try URL Encoder/Decoder Online →