URL Encode & Decode — Percent Encoding Explained

Last updated:

URL encoding (percent encoding, RFC 3986) replaces every character outside the 66 unreserved ASCII characters with a % sign followed by 2 hex digits — a space becomes %20, & becomes %26, and a 2-byte UTF-8 character like é produces 2 sequences (%C3%A9). JavaScript exposes 2 encoding functions with different scope: encodeURIComponent() encodes all characters except the 66 unreserved ones, while encodeURI() also preserves 18 structural characters like / ? # & = :; HTML forms use application/x-www-form-urlencoded and encode spaces as + instead of %20. This guide covers encodeURI vs encodeURIComponent,URLSearchParams for query strings, Python's urllib.parse functions (quote, quote_plus, urlencode), the %20 vs + space encoding difference, and 3 common mistakes including double-encoding.

URL encode or decode online

Paste any string to percent-encode it for URL use, or decode a URL-encoded string back to plain text — instantly in your browser.

Open URL Encoder / Decoder

What is URL encoding?

URLs can only contain a limited set of ASCII characters. Any character outside that set — spaces, accented letters, symbols, non-Latin scripts — must be percent-encoded before being placed in a URL. Each byte of the character is represented as %XX, where XX is the hexadecimal byte value.

Space   →  %20
/       →  %2F
?       →  %3F
#       →  %23
&       →  %26
=       →  %3D
+       →  %2B
@       →  %40
é       →  %C3%A9   (UTF-8: two bytes)

URL encoding in JavaScript

JavaScript provides two pairs of functions for URL encoding, each with different scope:

// encodeURIComponent — encode a single value (recommended for query params)
encodeURIComponent('hello world')   // "hello%20world"
encodeURIComponent('a=1&b=2')       // "a%3D1%26b%3D2"
encodeURIComponent('https://x.com') // "https%3A%2F%2Fx.com"

// decodeURIComponent — reverse of the above
decodeURIComponent('hello%20world') // "hello world"
decodeURIComponent('%C3%A9l%C3%A8ve') // "élève"

// encodeURI — encode a full URL (preserves :, /, ?, #, &, =)
encodeURI('https://example.com/path with spaces?q=hello world')
// "https://example.com/path%20with%20spaces?q=hello%20world"

// decodeURI — reverse of encodeURI
decodeURI('https://example.com/path%20with%20spaces')
// "https://example.com/path with spaces"

Use encodeURIComponent for individual query parameter values. Use encodeURI only for complete URLs where you want to preserve the URL structure characters.

Encode query parameters correctly in JavaScript

// Wrong — encodes the = and & signs, breaking the URL structure
const bad = encodeURIComponent('https://api.example.com/search?q=hello world&lang=en')

// Correct — encode only the values
const q = encodeURIComponent('hello world')
const lang = encodeURIComponent('en')
const url = `https://api.example.com/search?q=${q}&lang=${lang}`
// "https://api.example.com/search?q=hello%20world&lang=en"

// Even better — use URLSearchParams
const params = new URLSearchParams({ q: 'hello world', lang: 'en' })
const url2 = `https://api.example.com/search?${params}`
// "https://api.example.com/search?q=hello+world&lang=en"
// Note: URLSearchParams uses + for spaces, not %20

URL encoding in Python

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

# Encode a single value (safe='/' keeps slashes unencoded)
quote('hello world')          # 'hello%20world'
quote('hello world', safe='') # 'hello%20world'
quote('/path/to/file')        # '/path/to/file'  (/ preserved by default)
quote('/path/to/file', safe='') # '%2Fpath%2Fto%2Ffile'

# Decode
unquote('hello%20world')      # 'hello world'
unquote('%C3%A9l%C3%A8ve')    # 'élève'

# Encode query parameters (uses + for spaces, like HTML forms)
quote_plus('hello world')     # 'hello+world'
unquote_plus('hello+world')   # 'hello world'

# Encode a full query string from a dict
params = {'q': 'hello world', 'lang': 'en', 'page': 1}
urlencode(params)             # 'q=hello+world&lang=en&page=1'

%20 vs + for spaces

Spaces can be encoded as either %20 or +, depending on context:

ContextSpace encodingFunction
URL path segments%20encodeURIComponent
HTML form submissions (application/x-www-form-urlencoded)+URLSearchParams
Query strings (general)Either (prefer %20)

Modern APIs and browsers accept both, but %20 is more universally safe.

Characters that do NOT need encoding

These characters are safe in URLs and are left unencoded by encodeURIComponent:

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

All other characters — including /, ?, #,&, =, +, and any non-ASCII characters — are encoded when passed through encodeURIComponent.

Common URL encoding mistakes

  • Double-encoding — encoding an already-encoded string produces%2520 instead of %20. Always decode before re-encoding.
  • Using encodeURI on values — it preserves= and &, so query parameter values with those characters won't be properly escaped.
  • Not encoding Unicode — characters outside ASCII must be UTF-8 encoded first, then percent-encoded. Modern functions handle this automatically.

Decode a URL online

Paste any percent-encoded URL or string into the URL Encode / Decode tool to instantly encode or decode it. For Base64-encoded strings (common in JWTs and data URIs), use the Base64 tool instead.

Frequently asked questions

Which characters must be percent-encoded in URLs?

Only unreserved characters can appear unencoded: A–Z, a–z, 0–9, hyphen, underscore, period, and tilde. Everything else — spaces, /, ?, #, &, =, @, non-ASCII characters, and control characters — must be percent-encoded as %XX where XX is the hexadecimal byte value.

What is the difference between encodeURI and encodeURIComponent in JavaScript?

encodeURIComponent encodes all characters except unreserved ones — including /, ?, #, &, and =. Use it for individual query parameter values. encodeURI preserves URL structure characters like / ? # & and :. Use it only for full URLs. Never use encodeURI on query parameter values — it leaves = and & unencoded.

How do I URL-encode in Python?

Use urllib.parse: quote() for path segments (preserves / by default, encodes spaces as %20), quote_plus() for query values (encodes spaces as +), and urlencode() for full query strings from a dict. Decode with unquote() or unquote_plus() for +-encoded strings.

What is %20 in a URL?

%20 is the percent-encoded space character. Space is ASCII decimal 32, hexadecimal 20. URLs cannot contain literal spaces, so they must be encoded as %20 in path segments or as + in query strings (HTML form encoding). Both represent the same space character.

Can spaces be in URLs?

No. RFC 3986 does not allow literal spaces in URLs. Browsers auto-encode spaces in the address bar, but in code you must encode explicitly using encodeURIComponent in JavaScript or quote() in Python. Do not rely on client auto-encoding as behavior varies between environments.

What does + mean in a URL query string?

In a query string (after ?), + represents a space — an HTML form encoding convention. In the URL path, + is a literal plus sign. %20 works for spaces in both paths and query strings, making it the safer universal choice. Decode + as space using decodeURIComponent on query string values.