URL Encoding & Decoding Guide: Complete Reference
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
- Query parameters: Values in
?key=valueafter the? - Path segments: When path contains special characters
- Fragment identifiers: After
#in URLs - User input: Anything from forms, search boxes, or user-generated content
- International characters: Unicode text like Chinese, Arabic, or emojis
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
| Character | ASCII | Encoded | Reason |
|---|---|---|---|
| Space | 32 | %20 | Breaks URL structure |
| ! | 33 | %21 | Reserved in some contexts |
| # | 35 | %23 | Fragment delimiter |
| $ | 36 | %24 | Reserved |
| & | 38 | %26 | Parameter separator |
| + | 43 | %2B | Encoded as space in query strings |
| / | 47 | %2F | Path separator |
| : | 58 | %3A | Protocol separator |
| ? | 63 | %3F | Query string delimiter |
| [ | 91 | %5B | Reserved (IPv6) |
| ] | 93 | %5D | Reserved (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
| Character | Name | Use |
|---|---|---|
: | Colon | Protocol (http:), port (:8080) |
/ | Slash | Path segments |
? | Question mark | Query string start |
# | Hash | Fragment identifier |
[ ] | Brackets | IPv6 addresses |
@ | At sign | Userinfo, email |
& | Ampersand | Parameter separator in query |
= | Equals | Key-value separator |
+ | Plus | Historical: space in query strings |
$ | Dollar | Substitution 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%20teadecodeURI 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%20latteCommon 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%3F2. 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.pdf3. Email Address in URL
Input: "[email protected]" Output: "user%40example.com" URL: https://api.example.com/users?email=user%40example.com4. 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=30Query 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 automaticallyBuilding 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=organicParsing 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%ACPunycode 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.comIDN 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 domainsBest 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%27XSS 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
- Always encode user input before including in URLs
- Use modern APIs like URLSearchParams when possible
- Validate input before encoding (whitelist approach)
- Encode for the correct context: URL vs HTML vs JavaScript
- Use HTTPS to protect encoded data in transit
- Test with special characters:
<>"'()and Unicode
Encode and decode URLs instantly with the JieBang URL Encoder/Decoder tool.
Try URL Encoder/Decoder Online →