DD
DevDash
webdevjavascriptpythonbeginners

URL Encoding and Decoding: Complete Guide with Examples

URL Encoding and Decoding: Complete Guide with Examples

You have a URL with spaces, ampersands, or non-ASCII characters. If you drop them in raw, the URL breaks. URL encoding (also called percent-encoding) replaces unsafe characters with %XX hex codes so URLs work everywhere.

This guide covers the why, the how, and the subtle differences between encoding functions that trip people up.

Why URL Encoding Exists

URLs can only contain a specific set of characters defined by RFC 3986:

Unreserved: A-Z a-z 0-9 - _ . ~
Reserved:   : / ? # [ ] @ ! $ & ' ( ) * + , ; =

Everything else -- spaces, accented characters, emoji, most punctuation -- must be percent-encoded. A space becomes %20. An ampersand becomes %26. The Japanese character becomes %E6%97%A5.

If you do not encode these characters, different systems will interpret your URL differently. A space might be treated as the end of the URL. An & might be interpreted as a query parameter separator when you meant it as literal text.

JavaScript: encodeURIComponent vs encodeURI

This is the most common source of confusion. JavaScript has two encoding functions, and using the wrong one produces broken URLs.

encodeURIComponent -- For Values

Use this when encoding a part of a URL (a query parameter value, a path segment):

const searchQuery = 'hello world & goodbye';
const url = https://example.com/search?q=${encodeURIComponent(searchQuery)};
// https://example.com/search?q=hello%20world%20%26%20goodbye

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

It encodes / ? # & = + @ and all other special characters. This is what you want for parameter values, because those characters have meaning in URL structure.

encodeURI -- For Full URLs

Use this when you have a complete URL and just want to make it safe:

const url = 'https://example.com/path/to file?q=hello world';
encodeURI(url);
// https://example.com/path/to%20file?q=hello%20world

encodeURI does NOT encode: : / ? # [ ] @ ! $ & ' ( ) * + , ; =

It preserves the URL structure (protocol, slashes, query separator) and only encodes characters that are invalid everywhere.

When to Use Which

// CORRECT: Encoding a query value
const tag = 'C++ & C#';
const url = https://api.example.com/search?tag=${encodeURIComponent(tag)};
// https://api.example.com/search?tag=C%2B%2B%20%26%20C%23

// WRONG: encodeURI on a value (& is not encoded, breaks the URL) const url2 = https://api.example.com/search?tag=${encodeURI(tag)}; // https://api.example.com/search?tag=C++%20&%20C# <-- broken!

// CORRECT: Encoding a full URL with spaces const fullUrl = 'https://example.com/my path/file name.pdf'; encodeURI(fullUrl); // https://example.com/my%20path/file%20name.pdf

// WRONG: encodeURIComponent on a full URL (slashes get encoded) encodeURIComponent(fullUrl); // https%3A%2F%2Fexample.com%2Fmy%20path%2Ffile%20name.pdf <-- broken!

Rule of thumb: Use encodeURIComponent for values, encodeURI for complete URLs with known structure.

URLSearchParams (The Modern Way)

For query parameters, skip manual encoding entirely:

const params = new URLSearchParams({
  q: 'hello world',
  lang: 'C++',
  page: '1'
});

const url = https://example.com/search?${params}; // https://example.com/search?q=hello+world&lang=C%2B%2B&page=1

URLSearchParams handles encoding automatically. Note that it encodes spaces as + (which is valid in query strings per the HTML spec) rather than %20.

Decoding

decodeURIComponent('hello%20world%20%26%20goodbye');
// "hello world & goodbye"

decodeURI('https://example.com/my%20path/file%20name.pdf'); // "https://example.com/my path/file name.pdf"

// URLSearchParams decodes automatically const params = new URLSearchParams('q=hello+world&lang=C%2B%2B'); params.get('q'); // "hello world" params.get('lang'); // "C++"

Python: urllib.parse

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

Encode a path segment (spaces become %20)

quote('hello world & goodbye')

'hello%20world%20%26%20goodbye'

Encode a query value (spaces become +)

quote_plus('hello world & goodbye')

'hello+world+%26+goodbye'

Build query string from dict

urlencode({'q': 'hello world', 'lang': 'C++', 'page': 1})

'q=hello+world&lang=C%2B%2B&page=1'

Decode

unquote('hello%20world') # 'hello world' unquote_plus('hello+world') # 'hello world'

Python Equivalents to JavaScript

JavaScriptPythonSpaces
encodeURIComponent()quote(s, safe='')%20
encodeURI()quote(s, safe=':/?#[]@!$&\'()*+,;=')%20
URLSearchParamsurlencode()+

Characters That Need Encoding

Here is a quick reference for the most common characters:

CharacterEncodedWhy It Needs Encoding
Space%20 or +Terminates URLs in many contexts
&%26Separates query parameters
=%3DSeparates key from value
?%3FStarts query string
#%23Starts fragment/anchor
%%25Escape character itself
+%2BInterpreted as space in query strings
/%2FPath separator
@%40Username separator in URLs
"%22Terminates attribute values in HTML
< >%3C %3EHTML injection risk

Spaces: %20 vs + (The Eternal Debate)

Both are valid encodings for a space, but in different contexts:

  • %20 -- RFC 3986 standard. Valid everywhere in a URL (path, query, fragment).
  • + -- HTML form encoding (application/x-www-form-urlencoded). Valid only in query strings.

In practice, most servers accept both in query strings. But in URL paths, only %20 is correct. example.com/my+file.txt is a file literally named my+file.txt, not my file.txt.

Common Pitfalls

Double Encoding

// User input
const value = 'hello world';

// Encode once (correct) const encoded = encodeURIComponent(value); // 'hello%20world'

// Encode again (WRONG -- now %20 becomes %2520) const doubleEncoded = encodeURIComponent(encoded); // 'hello%2520world'

Double encoding happens when you pass already-encoded values through another encoding step. The % in %20 gets encoded to %25, producing %2520. The fix: encode once, at the point where you build the URL.

Encoding the Entire URL Including Protocol

// WRONG
encodeURIComponent('https://example.com/path?q=test');
// 'https%3A%2F%2Fexample.com%2Fpath%3Fq%3Dtest'

// RIGHT: Use encodeURI or build the URL from parts const base = 'https://example.com/path'; const params = new URLSearchParams({ q: 'test' }); const url = ${base}?${params};

Non-ASCII Characters

encodeURIComponent('cafe');    // 'cafe' (ASCII, no encoding needed)
encodeURIComponent('caf\u00e9'); // 'caf%C3%A9' (UTF-8 encoded)
encodeURIComponent('Tokyo');    // 'Tokyo'

URL encoding works on the UTF-8 byte representation. Multi-byte characters produce multiple %XX sequences.

Debugging URL Encoding Issues

When a URL is not working and you suspect encoding issues:

  1. Copy the full URL and decode it to see the raw values
  2. Check for double encoding (%25 appearing in the URL is a red flag)
  3. Verify spaces are encoded correctly for the context (path vs query)
  4. Check that & and = in parameter values are encoded

For quick encoding and decoding during development, devdash.io (or textshifter.com) has a URL encoder/decoder tool that shows the transformation in real time. Useful for debugging API calls where you are not sure if the encoding is correct.

Summary

// Encode a value for a query parameter
encodeURIComponent(value)

// Build query strings (handles encoding automatically) new URLSearchParams({ key: value }).toString()

// Encode a full URL (preserves structure) encodeURI(url)

// Decode decodeURIComponent(encodedValue)

The golden rule: encode values with encodeURIComponent, build query strings with URLSearchParams, and never encode the same string twice.

Related Tools

Want API access + no ads? Pro coming soon.