{ }jsonkitOpen the tool

URL Encoding Explained: Percent-Encoding, %20, and + Signs

8 min read · updated 2026-07-12

You have seen it a hundred times: a space in a link becomes %20, an ampersand becomes %26, a search for café turns into caf%C3%A9. That is URL encoding — also called percent-encoding — and it is what lets URLs carry characters that would otherwise break them.

This guide explains how percent-encoding works, which characters are safe and which must be escaped, the confusing case of + versus %20, and the difference between JavaScript's encodeURIComponent and encodeURI — the mix-up behind a huge share of malformed-URL bugs. It follows RFC 3986, the standard that defines URI syntax.

Why URLs Need Encoding

A URL is a compact grammar, not free-form text. Certain characters have structural jobs: ? starts the query string, # marks a fragment, & separates query parameters, / divides path segments, = pairs a key with a value. If your data contains one of those characters, putting it in raw would confuse the parser — a & inside a value looks exactly like the start of a new parameter.

URLs are also restricted to a small, safe set of ASCII characters. Spaces, most punctuation, and anything non-ASCII (accented letters, emoji, other scripts) are not allowed to appear literally. Percent-encoding is the escape mechanism that lets any byte ride inside a URL without ambiguity.

How Percent-Encoding Works

The rule is mechanical: take the byte you need to escape, and replace it with a % followed by that byte's value as two hexadecimal digits. A space is byte 0x20, so it becomes %20. An ampersand is 0x26, so it becomes %26. A # is 0x23 → %23.

Reserved and unsafe characters become %HH.text
Space  0x20  →  %20
#      0x23  →  %23
&      0x26  →  %26
?      0x3F  →  %3F
/      0x2F  →  %2F
=      0x3D  →  %3D

For non-ASCII characters, the character is first encoded to its UTF-8 bytes, and each byte is then percent-encoded individually. The letter é is two UTF-8 bytes, 0xC3 0xA9, so it becomes %C3%A9. That is why café turns into caf%C3%A9 — one character, two percent-escapes.

RFC 3986 also defines unreserved characters that never need encoding: the ASCII letters A–Z and a–z, digits 0–9, and the four symbols - _ . ~. Everything else is either reserved (structural) or unsafe, and should be percent-encoded when it appears as data rather than structure.

The '+' vs '%20' Confusion

Sometimes a space is %20, and sometimes it is +. Both can be correct — the difference is *where in the URL you are* and *which encoding rules apply*.

  • In the path and general URI syntax (RFC 3986), a space is always %20. A literal + here means an actual plus sign.
  • In a query string encoded as `application/x-www-form-urlencoded` — the format HTML forms submit — a space is encoded as +, and a literal plus becomes %2B.

Decoders mirror this: form-style decoders turn + back into a space, while a strict URI decoder leaves + as a plus. If you are ever unsure how a + will be read, prefer %20 for spaces and %2B for literal pluses — they mean the same thing to every decoder.

encodeURIComponent vs encodeURI

JavaScript ships two encoding functions, and picking the wrong one is one of the most common URL bugs. The difference is what each one considers 'structural' and therefore leaves alone.

FunctionPurposeLeaves reserved chars (/ ? & = #) alone?
encodeURIEncode a whole URLYes — keeps them so the URL still works
encodeURIComponentEncode a single value/componentNo — escapes them too

encodeURI assumes you are handing it a complete URL and must not break its structure, so it does *not* escape / ? & = # :. encodeURIComponent assumes you are handing it a single piece of data — a query value, a path segment — that must not be mistaken for structure, so it escapes those characters too. For anything you drop into a query parameter, you almost always want encodeURIComponent.

Use encodeURIComponent for values; the & would break the query otherwise.js
const q = "tom & jerry?";

encodeURI(q);
// "tom%20&%20jerry?"  — & and ? survive, WRONG inside a value

encodeURIComponent(q);
// "tom%20%26%20jerry%3F"  — fully escaped, safe as a value

// Building a URL: encode each value, not the whole thing
const url = `/search?q=${encodeURIComponent(q)}&page=1`;
// "/search?q=tom%20%26%20jerry%3F&page=1"
URLSearchParams encodes every value correctly (note it uses + for spaces).js
// The modern, hard-to-get-wrong approach:
const url = new URL("https://api.example.com/search");
url.searchParams.set("q", "tom & jerry?");
url.searchParams.set("page", "1");
url.toString();
// "https://api.example.com/search?q=tom+%26+jerry%3F&page=1"
%2FURL Encode / DecodeOpen the tool

URL Encoding and JSON in URLs

Passing JSON through a URL is a common need — a filter object, a state blob, a config for a shareable link. JSON is full of characters that are reserved or unsafe in URLs: {, }, ", :, ,, and spaces. Dropped in raw, they corrupt the query string. The fix is to percent-encode the entire JSON string as a single component before appending it.

Encode the whole JSON string as one component, decode then parse to reverse it.js
const filter = { tag: "news", limit: 10 };
const json = JSON.stringify(filter);
// '{"tag":"news","limit":10}'

const param = encodeURIComponent(json);
// '%7B%22tag%22%3A%22news%22%2C%22limit%22%3A10%7D'

const url = `/list?filter=${param}`;
// Decode on the other side:
const back = JSON.parse(decodeURIComponent(param));

For readable, verifiable results, decoding a URL locally beats guessing. A client-side encoder/decoder runs entirely in your browser, so you can paste an escaped URL, read the plain text, and confirm exactly what a parameter contains without sending it anywhere.

%2FURL Encode / DecodeOpen the tool

Frequently asked questions

Why does a space become %20 in a URL?

Spaces are not allowed to appear literally in a URL, so percent-encoding replaces the space (byte 0x20) with % followed by its two-digit hex value: %20. In query strings submitted by HTML forms (application/x-www-form-urlencoded), a space is instead encoded as +, but %20 is valid and unambiguous everywhere.

Does + always mean a space in a URL?

No. + means a space only in the query string when it is form-urlencoded (the format HTML forms use). In the URL path or under general RFC 3986 rules, + is a literal plus sign and a space is %20. To be safe, encode literal pluses as %2B and spaces as %20 so every decoder reads them the same way.

What is the difference between encodeURI and encodeURIComponent?

encodeURI is for encoding a whole URL and deliberately leaves structural characters like / ? & = # intact so the URL keeps working. encodeURIComponent is for a single value or component and escapes those characters too, so a value can't be mistaken for URL structure. For query-parameter values, use encodeURIComponent.

How do I put JSON in a URL?

Serialize the object with JSON.stringify, then wrap the whole string in encodeURIComponent before adding it as a query parameter — JSON's braces, quotes, colons, and commas are reserved or unsafe in URLs. To read it back, apply decodeURIComponent and then JSON.parse. Avoid this for sensitive data, since URLs are widely logged.

Is URL encoding the same as Base64?

No. Both make data safe to transmit, but percent-encoding escapes only the characters a URL can't carry, leaving safe characters readable, while Base64 re-encodes all binary into a 64-character alphabet. Neither is encryption. For binary data in text you often Base64 first; for making text safe inside a URL you percent-encode.

Which characters never need URL encoding?

RFC 3986's unreserved set: the letters A–Z and a–z, digits 0–9, and the four marks - _ . ~. These always represent themselves and can appear literally. Everything else is either reserved (structural, like / ? & =) or unsafe, and should be percent-encoded when used as data.