PureTools

URL Encoding: Why %20 Appears in Your URLs

PureTools Team· 8 min read
URL Encoding: Why %20 Appears in Your URLs

URL Encoding: Making URLs Safe for the Web

You've copied a URL and seen %20 where a space should be, or %C3%A9 where an accented character was. That's URL encoding (also called percent-encoding), and it exists because URLs can only contain a specific set of ASCII characters.

Why URLs Need Encoding

URLs have reserved characters with special meanings:

CharacterMeaning in URL
/Path separator
?Start of query string
&Query parameter separator
=Key-value separator
#Fragment identifier
:Scheme separator (http:)
@User info separator

If your data contains these characters, you must encode them to avoid ambiguity. Otherwise the browser can't tell if & is part of your data or a query separator.

How Encoding Works

Each unsafe character is replaced with % followed by its two-digit hex value:

CharacterEncodedHex Value
Space%200x20
!%210x21
#%230x23
&%260x26
+%2B0x2B
/%2F0x2F
é%C3%A9UTF-8: 0xC3 0xA9

Non-ASCII characters (accents, CJK, emoji) are first converted to UTF-8 bytes, then each byte is percent-encoded.

JavaScript: encodeURI vs encodeURIComponent

This is the single most confusing API in JavaScript:

// encodeURI — encodes a FULL URL
// Does NOT encode: : / ? # [ ] @ ! $ & ' ( ) * + , ; =
encodeURI('https://example.com/path with spaces?q=hello world')
// "https://example.com/path%20with%20spaces?q=hello%20world"

// encodeURIComponent — encodes a COMPONENT (value)
// DOES encode: : / ? # @ ! $ & ' ( ) * + , ; =
encodeURIComponent('price=10¤cy=USD')
// "price%3D10%26currency%3DUSD"

Rule of thumb:

  • Use encodeURIComponent() for query parameter values
  • Use encodeURI() only when encoding a complete URL that already has the correct structure
  • Better yet: use the URL and URLSearchParams APIs — they handle encoding automatically

The Modern Way: URL and URLSearchParams

// Build URLs safely — no manual encoding needed
const url = new URL('https://api.example.com/search');
url.searchParams.set('q', 'café & croissant');
url.searchParams.set('page', '1');
console.log(url.toString());
// "https://api.example.com/search?q=caf%C3%A9+%26+croissant&page=1"

// Parse existing URLs
const parsed = new URL('https://example.com/path?name=Jo%C3%A3o');
console.log(parsed.searchParams.get('name'));
// "João" — automatically decoded

Common Encoding Mistakes

Double encoding: Encoding an already-encoded string. %20 becomes %2520. This happens when you call encodeURIComponent on a value that was already encoded.

Not encoding at all: Passing raw user input into URLs. If someone's name is "O'Brien & Sons", your URL breaks.

Using + for spaces: In query strings, + means space (from HTML form encoding). But in path segments, + is a literal plus sign. %20 always means space everywhere.

# Python
from urllib.parse import quote, unquote, urlencode

quote('café & croissant')         # 'caf%C3%A9%20%26%20croissant'
unquote('caf%C3%A9')              # 'café'

# Build query strings
urlencode({'q': 'hello world', 'lang': 'pt'})
# 'q=hello+world&lang=pt'
// Go
import "net/url"

encoded := url.QueryEscape("café & croissant")
// "caf%C3%A9+%26+croissant"

decoded, _ := url.QueryUnescape(encoded)
// "café & croissant"

When to Encode What

Part of URLEncode WithExample
Full URLencodeURI() / new URL()https://example.com/my%20page
Query param valueencodeURIComponent() / URLSearchParams?q=caf%C3%A9
Path segmentencodeURIComponent()/users/Jo%C3%A3o
Form dataapplication/x-www-form-urlencodedname=Jo%C3%A3o&city=S%C3%A3o+Paulo

Encode/decode now: URL Encoder/Decoder — paste any text or URL, encode or decode instantly.