Why Spaces Become %20 in URLs (And When They Become +)
If you’ve ever debugged a broken query string, there’s a good chance you’ve run into this confusing situation:
hello world
suddenly becomes:
hello%20world
But sometimes it becomes:
hello+world
And that’s usually the moment developers start wondering:
- Which one is correct?
- Why are there two different formats?
- Does
%20mean the same thing as+? - Why does my backend decode one but not the other?
- Why does Postman behave differently from the browser?
The confusing part is that both %20 and + can represent spaces — but they come from different encoding rules and are used in different contexts.
This article breaks down where %20 comes from, why forms use +, and how these differences create real production bugs in APIs, redirects, and backend systems.
Why Spaces Need Encoding in URLs
URLs cannot safely contain literal spaces.
This is because spaces are not valid characters inside standard URLs.
For example, this URL is technically invalid:
https://example.com/search?q=hello world
Browsers may try to fix it automatically, but servers, proxies, and APIs can interpret it inconsistently.
So spaces must be encoded into a safe format before transmission.
Why Spaces Become %20
The %20 format comes from standard URL percent-encoding.
Under the hood:
- space = ASCII character 32
- decimal 32 = hexadecimal
20
So a space becomes:
%20
The % symbol simply means:
the next two characters are a hexadecimal byte
Example:
hello world
becomes:
hello%20world
This is the standard encoding used in:
- URLs
- path segments
- JavaScript encoding APIs
- browsers
- APIs
- RFC 3986 compliant systems
Why Spaces Sometimes Become +
This is where things get confusing.
The + symbol does not come from standard URL encoding.
It comes from HTML form encoding:
application/x-www-form-urlencoded
which browsers use when submitting forms.
The Historical Reason
Early web forms needed a compact way to encode spaces.
Instead of using:
%20
they used:
+
This convention became widely adopted in form submissions and query strings.
So:
hello world
became:
hello+world
inside form-encoded data.
The Important Distinction
| Format | Context |
|---|---|
%20 | Standard URL encoding |
+ | HTML form encoding |
Most developers discover this difference accidentally during debugging.
Real Example: Browser Form Submission
Suppose you have a basic HTML form:
<form method="GET">
<input name="q" value="hello world">
</form>
The browser usually sends:
?q=hello+world
—not:
?q=hello%20world
This surprises a lot of developers the first time they inspect raw network requests.
JavaScript Behaves Differently
Now compare that with JavaScript:
encodeURIComponent("hello world");
Output:
hello%20world
Notice:
- JavaScript uses
%20 - browser forms often use
+
Both can represent spaces, but not every backend handles them the same way.
The Bug That Happens in Production
This difference causes some genuinely annoying bugs.
Especially when:
- frontend apps
- backend APIs
- proxies
- older frameworks
- third-party services
all interpret encoding slightly differently.
Example: Backend Stores Literal +
Imagine this request:
?q=hello+world
Some frameworks correctly decode it into:
hello world
Others don’t.
The result becomes:
hello+world
stored literally in the database.
This used to happen frequently in:
- older Java stacks
- legacy PHP systems
- custom proxy layers
- improperly configured API gateways
Why + Can Be Dangerous
Another subtle issue:
The + character itself is a valid character.
So what if your actual value contains a literal plus sign?
Example:
C++
If improperly encoded:
C++
may accidentally decode into:
C
or:
C
depending on how the server interprets +.
Correct Way to Encode Literal +
A literal plus sign should become:
%2B
Example:
encodeURIComponent("C++");
Output:
C%2B%2B
This preserves the actual plus symbols safely.
Why Browsers and APIs Behave Differently
One of the most frustrating parts of debugging encoding issues is that different systems follow different rules.
For example:
| System | Space Encoding |
|---|---|
encodeURIComponent() | %20 |
| HTML forms | + |
URLSearchParams | + |
| Raw URL paths | %20 |
| Some backend frameworks | both |
| Some legacy systems | inconsistent |
This inconsistency is why URL bugs often survive local testing but fail in staging or production.
URLSearchParams and the + Surprise
Modern JavaScript introduces another subtle twist.
Example:
const params = new URLSearchParams({
q: "hello world"
});
console.log(params.toString());
Output:
q=hello+world
Not %20.
That’s because URLSearchParams follows form-urlencoded rules internally.
This surprises developers who expected consistency with encodeURIComponent().
Path Segments vs Query Parameters
Another important distinction:
Spaces in path segments should use:
%20
Example:
/products/hello%20world
Using + in paths is incorrect and can create routing problems.
Real Debugging Story: OAuth Redirect Failure
One of the nastiest bugs I encountered involved OAuth redirects.
The frontend encoded spaces using:
+
But the authentication provider expected:
%20
The redirect signature validation failed silently.
The error message?
invalid redirect_uri
No hint about encoding.
After tracing raw network logs for nearly an hour, the difference between + and %20 turned out to be the entire issue.
This kind of bug is surprisingly common in:
- OAuth
- SSO
- payment gateways
- signed URLs
- CDN token systems
How Different Languages Handle Spaces
JavaScript
encodeURIComponent("hello world");
Output:
hello%20world
Python
from urllib.parse import quote
quote("hello world")
Output:
hello%20world
Python Form Encoding
from urllib.parse import urlencode
urlencode({"q": "hello world"})
Output:
q=hello+world
PHP
urlencode("hello world");
Output:
hello+world
While:
rawurlencode("hello world");
produces:
hello%20world
This difference alone has caused countless cross-language inconsistencies.
Best Practices for Handling Spaces in URLs
Use %20 for Standard URL Encoding
Especially in:
- paths
- APIs
- redirects
- encoded URLs
Expect + in Form Data
Especially from:
- browser forms
URLSearchParams- older backend libraries
Always Encode Literal +
Use:
%2B
when the plus sign is actual data.
Avoid Manual String Concatenation
Prefer APIs like:
URLSearchParams
instead of manually building query strings.
Log Raw Requests During Debugging
This catches:
- double encoding
- malformed spaces
- proxy transformations
much faster than inspecting parsed objects.
Related Resources
If you're debugging malformed query strings or API requests, these tools and guides are useful:
-
URL Encoder/Decoder URL Encoder/Decoder
-
encodeURI vs encodeURIComponent Explained encodeURI vs encodeURIComponent
-
JSON Formatter JSON Formatter
FAQ
Why does space become %20?
Because spaces are not valid in standard URLs, so they are percent-encoded using hexadecimal ASCII values.
Why do forms use + instead of %20?
HTML forms use:
application/x-www-form-urlencoded
which historically represents spaces using +.
Are %20 and + always interchangeable?
Not always.
Many systems treat them similarly in query strings, but not in:
- path segments
- signed URLs
- OAuth flows
- some backend frameworks
Should I use %20 or + in URLs?
For standard URL encoding, use %20.
+ is mainly associated with form encoding.
Why does URLSearchParams use +?
Because it follows form-urlencoded behavior internally.
How do I encode a literal plus sign?
Use:
%2B
Example:
encodeURIComponent("C++");
Output:
C%2B%2B
Final Thoughts
The %20 vs + distinction looks tiny on paper, but it creates a surprising number of real-world bugs.
Especially once:
- browsers
- APIs
- proxies
- OAuth providers
- backend frameworks
all start interacting with each other.
The safest approach is understanding which encoding rules apply in which context instead of assuming all URL encoding behaves the same way.
And when debugging suspicious query strings or redirect URLs, it helps to inspect the raw encoded values directly instead of trusting browser output alone.
For quick testing and decoding during debugging sessions, this tool is genuinely handy: