Article · Mar 15, 2026 · 5 min read

Spaces in URLs: %20 vs +

The space character is the most common reason to URL-encode anything. And it’s also the source of one of URL encoding’s most persistent confusions: there are two valid encodings for a space, %20 and +, and the choice between them depends on which part of the URL you’re encoding.

This article covers both, when to use each, and the bugs that come from mixing them up.

The two encodings

%20 is the canonical percent-encoded space. It works everywhere in a URL — path, query, fragment, anywhere. It’s defined by RFC 3986.

+ is a legacy convention from HTML form submissions. It represents a space inside application/x-www-form-urlencoded data — which means inside query strings of forms submitted via GET, and inside POST bodies with that content type.

Both are valid URL encodings, but they live in different URL contexts.

Where each one works

URL part %20 +
Path componentspaceliteral +
Query string valuespacespace
Fragment (#)spaceliteral +
Form POST bodyspacespace

The key insight: in path components and fragments, only %20 means space. + means literal plus sign. In query strings and form bodies, both + and %20 are accepted as space by most servers.

How browsers handle it

When you type a URL with a space directly into the address bar, browsers encode it as %20:

You type:        https://example.com/My Document
Browser sends:   https://example.com/My%20Document

When you submit an HTML form via GET, browsers encode spaces in values as +:

<form action="/search" method="get">
  <input name="q" value="hello world">
</form>

Browser sends:   /search?q=hello+world

This is why you see both in the wild — browsers themselves use both conventions depending on context.

What happens when you mix them up

Using + in a path

// Bad
const url = "/users/" + encodeURIComponent("John Doe").replace(/%20/g, '+');
// "/users/John+Doe"
// Server sees: user with name literally "John+Doe"
// — not "John Doe" — because + is literal in paths

Decoding a query value that has +

// User submitted form value: "C++ programming"
// Browser encoded as: q=C%2B%2B+programming
// Note: the literal + in C++ became %2B, the space became +

// Wrong decoding (with + as literal)
decodeURIComponent("C%2B%2B+programming")
// → "C++ +programming"   ← extra plus where space should be

// Right decoding (form-encoded variant: + → space)
"C%2B%2B+programming".replace(/\+/g, ' ').then(decodeURIComponent);
// → "C++ programming"

JavaScript’s decodeURIComponent doesn’t treat + as space. You have to handle that yourself or use URLSearchParams (which does).

The decoder behavior split

Different language decoders handle + differently:

Language Function + becomes
JavaScriptdecodeURIComponentliteral +
JavaScriptURLSearchParamsspace
Pythonunquoteliteral +
Pythonunquote_plusspace
PHPurldecodespace
PHPrawurldecodeliteral +
JavaURLDecoder.decodespace
Gourl.QueryUnescapespace

The pattern: functions named with “query” or “plus” treat + as space (form-encoded variant). Functions named “raw” or just URL leave + literal.

Encoding side: which function produces which

Language Function Space becomes
JavaScriptencodeURIComponent%20
JavaScriptURLSearchParams+
Pythonquote%20
Pythonquote_plus+
PHPurlencode+
PHPrawurlencode%20
JavaURLEncoder.encode+
C#Uri.EscapeDataString%20

Which one should you use?

For new code, prefer %20 everywhere. Reasoning:

Use + when you specifically need application/x-www-form-urlencoded compatibility:

What about literal plus signs?

If your data has a real + character (like “C++ programming”), encoding depends on context:

// In a path: + is already literal, but encoding it is safer
"/courses/C%2B%2B"
"/courses/C++"   // also accepted

// In a query value: must be encoded if you want literal +
"?q=C%2B%2B+programming"   // C++ programming
"?q=C+++programming"       // 3 spaces and "programming"  ← wrong

Quick decision tree

You’re encoding a space. Which to use?

You’re decoding a string. Was it form-encoded?


Found this useful? Try the URL decoder, the URL encoder, or browse all tools.

More reading

From the blog.