DevToolsForYou
Encoding

How to URL Encode and Decode

A practical guide to percent-encoding — understand which characters need encoding, how the % notation works, and how to encode and decode URLs in JavaScript, Python, and the terminal.

3 min readUpdated Apr 11, 2026

What is URL encoding?

URLs can only contain a limited set of ASCII characters. Any character outside that safe set — spaces, Unicode, reserved delimiters like & and = — must be percent-encoded: replaced with a % followed by the character's two-digit hexadecimal UTF-8 byte value. This process is formally called percent-encoding and is defined in RFC 3986.

Which characters are safe?

Unreserved characters are always safe and never encoded: A–Z, a–z, 0–9, and the four symbols - _ . ~. Everything else is either a reserved character (like / ? # & =) that has structural meaning in a URL, or an unsafe character that must be encoded. A space encodes to %20 (or + in query strings under HTML form encoding).

text
Safe (unreserved):  A-Z  a-z  0-9  -  _  .  ~
Reserved (special): :  /  ?  #  [  ]  @  !  $  &  '  (  )  *  +  ,  ;  =
Must encode:        space → %20   < → %3C   > → %3E   " → %22   { → %7B

encodeURI vs encodeURIComponent

JavaScript has two encoding functions with different scopes. encodeURI() encodes a full URL and leaves reserved characters alone (so / and ? stay intact). encodeURIComponent() encodes a single value — it encodes reserved characters too, making it the right choice for query parameter values.

javascript
const url = "https://example.com/search?q=hello world&lang=en";

// encodeURI — safe for a full URL
encodeURI(url);
// "https://example.com/search?q=hello%20world&lang=en"
// ↑ / ? & = are preserved — they are structural

// encodeURIComponent — safe for a query value
const query = "C++ & Java";
encodeURIComponent(query);
// "C%2B%2B%20%26%20Java"
// ↑ + and & are encoded — they would break the query string

// Decode
decodeURIComponent("C%2B%2B%20%26%20Java"); // "C++ & Java"

Encoding in Python

Python's urllib.parse module provides quote() for encoding and unquote() for decoding. Use quote() for path segments and quote_plus() for HTML form-style query strings (which use + for spaces).

python
from urllib.parse import quote, unquote, urlencode

# Encode a path segment
quote("hello world/path")       # "hello%20world/path"  (/ is safe by default)
quote("hello world/path", safe="")  # "hello%20world%2Fpath"  (encode / too)

# Encode a query string
urlencode({"q": "C++ & Java", "page": 1})
# "q=C%2B%2B+%26+Java&page=1"

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

Encoding from the command line

There is no built-in URL-encode command on most systems, but Python and curl make it easy.

bash
# Encode using Python one-liner
python3 -c "from urllib.parse import quote; print(quote('hello world & more'))"
# hello%20world%20%26%20more

# Decode using Python one-liner
python3 -c "from urllib.parse import unquote; print(unquote('hello%20world'))"
# hello world

# curl --data-urlencode to send encoded form values
curl -G https://httpbin.org/get --data-urlencode "q=hello world"

Common pitfalls

Double-encoding is the most frequent mistake: encoding an already-encoded string converts % to %25, turning %20 into %2520. Always decode before re-encoding. A second pitfall is using encodeURI() on query parameter values — it will not encode & and =, breaking the query string.

javascript
// Double-encoding bug
encodeURIComponent("%20");  // "%2520"  ← wrong, was already encoded

// Fix: only encode raw values, never pre-encoded strings
const raw = "hello world";
encodeURIComponent(raw);    // "%20" ← correct
Frequently asked questions

What is the difference between %20 and + in a URL?

%20 is the standard percent-encoding for a space (RFC 3986). The + sign represents a space only inside HTML form query strings (application/x-www-form-urlencoded), which is a different encoding scheme. In a URL path, a literal + means a plus sign, not a space.

Should I encode the entire URL or just the query parameters?

Encode individual values before assembling the URL. If you encode the full URL after construction, you will encode structural characters like / and ? that should stay literal.

Why does my encoded URL show %25 instead of %?

You are double-encoding. The string already contained a % character, and encoding it again turned % into %25. Decode first, then re-encode from the raw value.

Related cheatsheetsAll cheatsheets →
Related guidesAll guides →