%20 vs + in URLs: How Spaces Are Encoded (and When Each Breaks)
Why spaces in URLs appear as %20 in some places and + in others, which one to use in paths and query strings, and how to fix a + that turns into a space.
The short answer
Both %20 and + can represent a space, but in different places. %20 is the general percent-encoding for a space and is safe everywhere in a URL. + means a space only inside a query string that uses the HTML form format (application/x-www-form-urlencoded); in a URL path, a + is just a plus sign. When in doubt, use %20, and always encode a real plus sign as %2B. The free URL Encoder/Decoder produces %20 for spaces and %2B for plus signs, so its output is safe in both places.
Where the two formats come from
There are two related but different encoding rules on the web:
% followed by its byte value in hex. A space is byte 32, which is 20 in hex, so it becomes %20.+ to keep URLs short and readable. A real plus sign then has to be written as %2B.Both systems agree that %20 is a space. They disagree about what a bare + means.
What each encoding function produces
Different languages and functions pick different rules. These outputs were produced by running the functions on the text hello world & more+1:
| Function | Output | Space becomes | Plus becomes |
|---|---|---|---|
JavaScript encodeURIComponent | hello%20world%20%26%20more%2B1 | %20 | %2B |
JavaScript URLSearchParams | hello+world+%26+more%2B1 | + | %2B |
Python urllib.parse.quote | hello%20world%20%26%20more%2B1 | %20 | %2B |
Python urllib.parse.quote_plus | hello+world+%26+more%2B1 | + | %2B |
All four are valid in a query string. Only the %20 versions are also safe in a path.
Decoding is where the bugs appear
Encoding rarely goes wrong. Decoding with the wrong rule does. Look at what happens to the string a+b%20c:
- JavaScript
decodeURIComponentgivesa+b c. It decodes%20but leaves+as a plus sign. - Python
unquotealso givesa+b c. - Python
unquote_plusgivesa b c. It treats+as a space. URLSearchParamsin JavaScript also treats+as a space when it reads a query string.
So the same URL can produce different values depending on which function your server or script uses.
The classic bug: a phone number or email loses its plus sign
Suppose a link is built by hand as /search?phone=+919876543210. A server that reads query strings with form decoding sees + as a space and receives 919876543210, with a leading space and no plus. The same happens with email addresses that use plus addressing, such as name+news@example.com.
Fix: encode the value before putting it in the URL. With encodeURIComponent, the plus becomes %2B and every decoder turns it back into +.
The opposite bug: a + shows up where a space should be
If a query string was built with form encoding (q=hello+world) but your code reads it with decodeURIComponent, you get hello+world with a literal plus. Either read query strings with a form-aware parser (URLSearchParams in JavaScript, parse_qs or unquote_plus in Python), or replace + with a space before decoding a value you know came from a form.
How to use the URL Encoder for this
+91 98765 43210, not the whole address.%2B91%2098765%2043210. Paste it after = in your query string.Two things to know about this tool, because it uses the browser's encodeURIComponent and decodeURIComponent functions:
- It encodes every reserved character, including
/,?,&,=and:. That is what you want for a single value. If you paste a whole URL, the result will be one long encoded string that no longer works as a link. Encode the parts, then assemble the URL. - Decode mode leaves + as a plus sign. If you are decoding a query string that came from an HTML form, replace each
+with a space first.
For a step-by-step guide to encoding a single parameter, see how to URL-encode a query parameter.
Which should you use?
| Situation | Use |
|---|---|
| A space in a URL path, such as a file name | %20 |
| A space in a query string value you build yourself | %20 (always works) |
| Reading a query string sent by an HTML form | Expect + and decode it as a space |
| A real plus sign anywhere in a value | %2B |
| Building a whole query string in JavaScript | URLSearchParams (it handles the + rule both ways) |
Common mistakes
- Encoding a URL twice.
%20encoded again becomes%2520, because%itself is encoded as%25. If you see%25in a link, something encoded an already-encoded value. - Encoding the whole URL with encodeURIComponent. The
://and/separators get encoded too and the link breaks. Use it on values only. - Replacing spaces with + in file paths. A file called
my file.pdfmust bemy%20file.pdfin the path.my+file.pdfasks the server for a file with a plus in its name. - Pasting a stray % sign. A value like
100%is not valid encoded text. Encode it first:100%25.
Quick test
Paste café & crème into the encoder. You should get caf%C3%A9%20%26%20cr%C3%A8me. The accented letters become two bytes each because the encoder uses UTF-8, and the space is %20, never +.
For the bigger picture of why URLs need encoding at all, read URL encoding and decoding explained. If you are also passing binary data in a URL, the Base64 encoder is the usual next step, but remember that standard Base64 output can itself contain + and /.
Frequently asked questions
Is %20 or + correct for a space in a URL?
%20 is correct everywhere in a URL. A plus sign means a space only in query strings that use HTML form encoding. In a URL path, + is a literal plus sign, so use %20 for spaces in paths.
Why does a plus sign in my query string turn into a space?
The server decodes the query string with form rules, where + means space. Encode a real plus sign as %2B before adding it to the URL, for example with encodeURIComponent or a URL encoder tool.
Does encodeURIComponent turn spaces into + or %20?
It turns spaces into %20 and plus signs into %2B. URLSearchParams and Python quote_plus use + for spaces instead.
What does %2520 mean in a URL?
It means the URL was encoded twice. %25 is the encoding of the % sign, so %20 encoded again becomes %2520. Decode once to get back %20, then fix the code that encodes twice.
Put this guide into practice with our free online tool — no signup required.
Open tool