Skip to main content
DevBench
All articles
webencodingjavascript

URL Encoding Explained: What %20, +, and Percent-Encoding Mean

June 27, 20265 min read

Open your browser's network panel and look at a real URL. You will see strings like Hello%20World, price%3D99, or tag=c%2B%2B. These are percent-encoded characters — a transformation applied because URLs can only safely contain a limited set of ASCII characters.

Why URL encoding exists

The URI specification (RFC 3986) defines a small set of unreserved characters that are always safe in a URL: A–Z a–z 0–9 - _ . ~. Everything else — spaces, slashes, equals signs, question marks, Unicode characters — must be percent-encoded before being placed in a URL component.

Percent-encoding works by replacing the character with % followed by its two-digit hexadecimal UTF-8 byte value. A space (byte 0x20) becomes %20. The / character (byte 0x2F) becomes %2F.

Reserved vs unreserved characters

CategoryCharactersRule
UnreservedA-Z a-z 0-9 - _ . ~Never encoded — always safe
Reserved (general delimiters): / ? # [ ] @Have structural meaning — encode when used as data
Reserved (sub-delimiters)! $ & ' ( ) * + , ; =Have component-specific meaning — encode when used as data
Everything elseSpaces, Unicode, <>, % itselfMust always be encoded

%20 vs + for spaces

You will see both %20 and + used for spaces, and they are not interchangeable:

  • %20 is the correct percent-encoding of a space in any URL component (path, query, fragment)
  • + means a space only in application/x-www-form-urlencoded bodies — the format used by HTML forms. It does NOT mean a space in a path segment.

If you are building a URL by hand, always use %20 for spaces. Use + only when serialising HTML form data.

Common percent-encoded characters

CharacterEncodedWhere it appears
Space%20Search queries, file names with spaces
/%2FSlash inside a path segment (not as path separator)
?%3FLiteral question mark in query value
=%3DEquals sign in a query value
&%26Ampersand in a query value
+%2BLiteral plus in query value (to avoid space confusion)
#%23Hash in a query value (prevents fragment confusion)
€ (U+20AC)%E2%82%ACNon-ASCII — UTF-8 bytes, each percent-encoded

encodeURIComponent vs encodeURI

JavaScript has two built-in encoding functions. The key difference is what they leave alone:

  • encodeURIComponent(str) — encodes everything except A-Z a-z 0-9 - _ . ! ~ * ' ( ). Use this for individual query parameter values — it encodes /, ?, &, =, and #.
  • encodeURI(str) — leaves reserved characters alone (assumes the input is already a complete URL). Use this only if you have a full URL and want to encode stray non-ASCII characters in it without breaking the URL structure.
// Building a URL with query params — use encodeURIComponent
const q = encodeURIComponent("C++ programming & design");
const url = `https://example.com/search?q=${q}`;
// https://example.com/search?q=C%2B%2B%20programming%20%26%20design

// Using encodeURI on an existing URL — leaves / ? & = intact
encodeURI("https://example.com/path with spaces?q=hello");
// https://example.com/path%20with%20spaces?q=hello

Decoding

The reverse functions are decodeURIComponent() and decodeURI(). Use decodeURIComponent() for individual query values. Calling decodeURIComponent() on a full URL will decode structural characters like %2F back to /, which may break path parsing.

Always decode query values on the server side — never in a browser URL bar. Most server frameworks (Express, FastAPI, Laravel) decode query parameters automatically.

Try it yourself

Use the free browser-based URL Encoder / Decoder on DevBench — no signup, runs entirely in your browser.

Open URL Encoder / Decoder