URL Encoding: A Developer's Complete Reference

posted 5 min read

URL encoding (percent-encoding) is one of those foundational web concepts that causes real bugs when misunderstood. Here's how it works, when you need it, and the correct functions to use in different languages.

What is URL encoding?

A URL may only contain a restricted set of ASCII characters. Characters outside this set — including spaces, special symbols, accented letters, and non-ASCII characters — must be encoded as %XX where XX is the hexadecimal byte value.

Safe characters (don't need encoding): Letters (A–Z, a–z), digits (0–9), and: - _ . ~

Reserved characters (have special meaning in URLs): : / ? # [ ] @ ! $ & ' ( ) * + , ; =

Reserved characters must be encoded when they appear as data (not as structural URL characters). For example, a query string value containing & must be encoded as %26 — otherwise it breaks the URL structure.

Common encodings:

| Character | Encoded |

|-----------|---------|

| Space | %20 or + (in form data) |

| & | %26 |

| = | %3D |

| + | %2B |

| # | %23 |

| / | %2F |

| ? | %3F |

| @ | %40 |

| : | %3A |

For quick encoding and decoding: URL Encoder / Decoder handles all of this in the browser without code.

JavaScript: which function to use

encodeURI(url): Encodes a complete URL. Does NOT encode characters that are valid URL structure characters: : / ? # [ ] @ ! $ & ' ( ) * + , ; =

Use this when you have a full URL and want to make it safe without breaking its structure.

``javascript

encodeURI('https://example.com/search?q=hello world&lang=en')

// → "https://example.com/search?q=hello%20world&lang=en"

// Note: spaces encoded, but ? & = left as-is

`

encodeURIComponent(value): Encodes everything except letters, digits, and - _ . ! ~ * ' ( ). Encodes ALL special characters including /, ?, #, &, =.

Use this when encoding individual query parameter values or path segments.

`javascript
encodeURIComponent('hello world & more')


// → "hello%20world%20%26%20more"


// Note: & is now %26

encodeURIComponent('a=b&c=d')

// → "a%3Db%26c%3Dd"

`

Building a URL from parts correctly:

`javascript
const base = 'https://api.example.com/search';</p>
const params = {


q: 'hello world',


category: 'web & mobile',


page: 1


};

const queryString = Object.entries(params)

.map(([k, v]) => ${encodeURIComponent(k)}=${encodeURIComponent(v)})

.join('&');

const url = ${base}?${queryString};

// "https://api.example.com/search?q=hello%20world&category=web%20%26%20mobile&page=1"

`

Or use URLSearchParams:

<code>javascript <p>const url = new URL('https://api.example.com/search');</p> <p>url.searchParams.set('q', 'hello world');</p> <p>url.searchParams.set('category', 'web & mobile');</p> <p>console.log(url.toString());</p> <p>// "https://api.example.com/search?q=hello+world&category=web+%26+mobile"</p> </code>

Note: URLSearchParams uses + for spaces in the query string (application/x-www-form-urlencoded format), not %20. This is technically correct for query strings but may differ from what some servers expect.

Decoding:

`javascript
decodeURIComponent('hello%20world%20%26%20more')


// → "hello world & more"

decodeURI('https://example.com/search?q=hello%20world')

// → "https://example.com/search?q=hello world"

`

Python

`python
from urllib.parse import quote, unquote, urlencode, parse_qs

Encode a query parameter value

quote('hello world & more', safe='')

→ 'hello%20world%20%26%20more'

safe='' encodes everything including /

Encode a full URL (leave / and : intact)

quote('https://example.com/path?q=hello world', safe=':/?#[]@!$&\'()*+,;=')

→ 'https://example.com/path?q=hello%20world'

Encode multiple parameters as a query string

params = {'q': 'hello world', 'category': 'web & mobile', 'page': 1}

urlencode(params)

→ 'q=hello+world&category=web+%26+mobile&page=1'

Note: urlencode uses + for spaces (form data encoding)

Decode

unquote('hello%20world%20%26%20more')

→ 'hello world & more'

Parse a query string

parse_qs('q=hello+world&page=2')

→ {'q': ['hello world'], 'page': ['2']}

`

PHP

`php
// Encode individual values (for query strings and form data)


urlencode('hello world & more')


// → 'hello+world+%26+more'

// Encode individual values (raw percent-encoding, no + for spaces)

rawurlencode('hello world & more')

// → 'hello%20world%20%26%20more'

// Decode

urldecode('hello+world+%26+more')

// → 'hello world & more'

rawurldecode('hello%20world%20%26%20more')

// → 'hello world & more'

// Build a query string

$params = ['q' => 'hello world', 'page' => 1];

http_build_query($params)

// → 'q=hello+world&page=1'

`

Command line

`bash
Encode
python3 -c "from urllib.parse import quote; print(quote('hello world & more', safe=''))"


→ hello%20world%20%26%20more

Decode

python3 -c "from urllib.parse import unquote; print(unquote('hello%20world%20%26%20more'))"

→ hello world & more

With curl (automatically encodes for you)

curl "https://api.example.com/search?q=hello world" # curl handles encoding

Or explicitly:

curl --data-urlencode "q=hello world & more" https://api.example.com/search

`

Common mistakes

1. Double encoding

Encoding an already-encoded URL results in the % signs being encoded again:

  • hello%20worldhello%2520world (wrong)

Only encode raw data, not already-encoded URLs.

2. Using encodeURI on a query parameter value

encodeURI doesn't encode & or = — it's designed for full URLs, not individual values. Using it on a query parameter value will corrupt the URL.

`javascript
// WRONG: & is not encoded


const url = https://api.example.com?q=${encodeURI('a=b&c=d')}<code>;</p>

// → "https://api.example.com?q=a=b&c=d" (breaks the URL)

// CORRECT

const url = https://api.example.com?q=${encodeURIComponent('a=b&c=d')};

// → "https://api.example.com?q=a%3Db%26c%3Dd"

`

3. Forgetting to decode server-side

Most web frameworks automatically decode query string parameters, but manual URL parsing may not. Always decode before using values.

4. The + vs %20 confusion

In application/x-www-form-urlencoded (HTML form data), spaces encode as +. In standard URLs, spaces encode as %20. Some APIs return errors if you mix these. Check the API docs — if they show + in examples, they expect form encoding.


URL encoding is simple in principle but has enough edge cases (double encoding, the + vs %20 distinction, encodeURI vs encodeURIComponent`) that it's worth knowing exactly which function to use in each context.

Originally published at https://snappytools.app/url-encoder-decoder/

More Posts

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolio - Apr 1

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelskiverified - Apr 23

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

2 comments
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!