{ }jsonkitOpen

What is URL encoding? Percent-encoding, %20 and + explained

8 min read · updated 2026-07-12

URL encoding, also called percent-encoding, replaces characters that a URL cannot carry with a % and two hex digits. A space in a link becomes %20, an ampersand becomes %26, and a search for café turns into caf%C3%A9. It 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 for a space, and the difference between JavaScript's encodeURIComponent and encodeURI. Mixing up those two causes many malformed URL bugs. The guide follows RFC 3986, the standard that defines URI syntax.

Why URLs need encoding

A URL has a strict grammar. Some characters have structural jobs: ? starts the query string, # marks a fragment, & separates query parameters, / divides path segments, and = pairs a key with a value. If your data contains one of these characters, putting it in raw confuses the parser. A & inside a value looks exactly like the start of a new parameter.

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

How percent-encoding works

The rule is mechanical. Take the byte you need to escape and replace it with % followed by the 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, so it becomes %23.

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

So when you see %2F in a URL, it is an encoded slash that belongs to the data, not a path separator. A colon encodes to %3A. You only need to encode it when it appears inside a value, because in https: it is part of the structure.

Non-ASCII characters are first encoded to their UTF-8 bytes, and then each byte is percent-encoded. 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, the digits 0-9, and the four symbols - _ . ~. Every other character is either reserved (structural) or unsafe, and should be percent-encoded when it appears as data rather than structure.

Space in a URL: + vs %20

Sometimes a space is %20, and sometimes it is +. Both can be correct. It depends on *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 follow the same split. Form-style decoders turn + back into a space, and a strict URI decoder leaves + as a plus. If you are unsure how a + will be read, use %20 for spaces and %2B for literal pluses. Every decoder reads those the same way.

encodeURIComponent vs encodeURI

JavaScript has two encoding functions, and picking the wrong one is a very common URL bug. They differ in which characters they treat as structural and leave alone.

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

encodeURI assumes it has a complete URL whose structure must stay intact, so it does *not* escape / ? & = # :. encodeURIComponent assumes it has a single piece of data, such as a query value or a path segment, that must not be mistaken for structure, so it escapes those characters too. For anything you put 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"
URL Encode / DecodeOpen the tool→

URL encoding and JSON in URLs

Passing JSON through a URL is common: 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. Put in raw, they corrupt the query string. The fix is to percent-encode the whole JSON string as a single component before you append 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));

To check what an escaped URL contains, decode it instead of guessing. The encoder and decoder below run in your browser, so you can paste a URL, read the plain text, and see exactly what each parameter holds without sending it anywhere.

URL Encode / DecodeOpen the tool→

Frequently asked questions

Why does a space become %20 in a URL?

A URL cannot contain a literal space, so percent-encoding replaces the space (byte 0x20) with % and its two-digit hex value: %20. In query strings submitted by HTML forms (application/x-www-form-urlencoded), a space is encoded as + instead, but %20 is valid and unambiguous everywhere.

Does + always mean a space in a URL?

No. + means a space only in a form-urlencoded query string, 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 do %2F and %3A mean in a URL?

%2F is an encoded slash (/) and %3A is an encoded colon (:). They appear when a slash or colon is part of a value, such as a file path or a time in a query parameter, rather than part of the URL's structure. Decoding turns them back into / and :.

What is the difference between encodeURI and encodeURIComponent?

encodeURI encodes a whole URL and leaves structural characters like / ? & = # alone so the URL keeps working. encodeURIComponent encodes a single value or component and escapes those characters too, so the value cannot 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 send, but percent-encoding escapes only the characters a URL cannot carry and leaves the rest readable. Base64 re-encodes all bytes into a 64-character alphabet. Neither is encryption. Use Base64 to put binary data in text, and percent-encoding to make text safe inside a URL.

Which characters never need URL encoding?

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