Menu

URL encoding explained: what %20 and other percent codes mean

URL encoding (percent-encoding) replaces a character that cannot appear as-is in a URL with a percent sign and two hexadecimal digits for each of its bytes. %20 is a space, %26 is an ampersand and %2F is a slash.

Published

URL encoding, also called percent-encoding, is the way a URL carries characters that would otherwise break it or be misread. Each such character is turned into bytes, and each byte is written as % followed by two hexadecimal digits. A space becomes %20, & becomes %26 and é becomes %C3%A9. A server or browser decodes the codes back into the original text. The rules come from RFC 3986, the internet standard for URI syntax, and from the WHATWG URL Standard that browsers follow. To encode or decode text yourself, use the URL encoder and URL decoder. Both run in your browser.

How a percent code is built

RFC 3986 (section 2.1) defines a percent-encoded byte as % plus two hex digits (HEXDIG HEXDIG), and gives %20 for the space as its example. The two digits are the byte’s value in base 16. The space is character 32, which is 20 in hex, so it is written %20.

Characters outside basic ASCII take more than one byte. RFC 3986 (section 2.5) says text should first be converted to bytes with UTF-8, and then every byte that is not an unreserved character is percent-encoded. The WHATWG URL Standard also uses UTF-8. That is why one accented letter turns into two codes and an emoji into four:

Character UTF-8 bytes Encoded
é C3 A9 %C3%A9
€ E2 82 AC %E2%82%AC
😀 F0 9F 98 80 %F0%9F%98%80

The hex digits can be upper or lower case. RFC 3986 treats them as the same but says producers “should use uppercase hexadecimal digits”. If you want to see the bytes behind any text without the percent signs, the text to hex converter shows them.

Which characters get encoded

RFC 3986 splits characters into three groups:

  • Unreserved (section 2.3): letters A–Z a–z, digits 0–9 and - . _ ~. These never need encoding.
  • Reserved (section 2.2): characters with a job in the URL’s structure. The general delimiters are : / ? # [ ] @ and the sub-delimiters are ! $ & ' ( ) * + , ; =. They stay as they are when they do that job (the ? that starts a query, the & between parameters). They are encoded when they are part of the data, such as an & inside a company name.
  • Everything else: spaces, quotes, < >, % itself, control characters and all non-ASCII text. These are always encoded.

These are the codes you will meet most often. The output was produced with JavaScript’s encodeURIComponent, which the encoder’s default mode uses:

Character Code Character Code
space %20 / %2F
" %22 : %3A
# %23 ; %3B
$ %24 = %3D
% %25 ? %3F
& %26 @ %40
+ %2B [ %5B
, %2C ] %5D

A line break in the text becomes %0A. For example, hello world followed by a new line and Q&A encodes to hello%20world%0AQ%26A.

Space as %20 or +

The plus sign for a space comes from HTML forms, not from RFC 3986. When a form is submitted with the GET method, the browser sends the fields in a format called application/x-www-form-urlencoded. The WHATWG URL Standard defines it: the serializer writes spaces as +, and the parser replaces every + with a space. The standard itself calls the format “an aberrant monstrosity”, the result of years of compromises kept for compatibility.

The practical rules:

  • In a path (/files/my%20report.pdf), a space must be %20. A + there is a literal plus sign.
  • In a query string read as form data (?q=red+shoes), + means a space, so a real plus sign must be sent as %2B. Otherwise C++ arrives as C with two spaces.
  • %20 works in a query string too, so it is the safer choice when you are not sure.

The URL encoder’s Form data (spaces as +) mode writes the format the way browsers’ URLSearchParams does: spaces become + (hello world becomes hello+world), and ! ' ( ) ~, which encodeURIComponent leaves alone, are escaped as well (!'()~ becomes %21%27%28%29%7E). Servers decode both versions to the same text. When decoding, the decoder’s Treat + as a space (query strings) option is on by default. Turn it off for paths, or a+b would become a b.

encodeURI vs encodeURIComponent

JavaScript has two built-in encoders, and the encoder offers both as modes.

A value (encodeURIComponent) encodes everything except A–Z a–z 0–9 - _ . ! ~ * ' ( ), according to MDN. Use it for one piece of a URL: a search term, a parameter value, a file name.

A whole URL (encodeURI) also leaves the reserved characters ; / ? : @ & = + $ , # alone, so a complete address keeps its structure. Use it to clean up a full link that has spaces or accents in it.

The difference on the tool’s sample text, café & crème brûlée/50% off?:

Mode Output
A value (encodeURIComponent) caf%C3%A9%20%26%20cr%C3%A8me%20br%C3%BBl%C3%A9e%2F50%25%20off%3F
A whole URL (encodeURI) caf%C3%A9%20&%20cr%C3%A8me%20br%C3%BBl%C3%A9e/50%25%20off?
Form data (spaces as +) caf%C3%A9+%26+cr%C3%A8me+br%C3%BBl%C3%A9e%2F50%25+off%3F

Using the whole-URL mode on a value is a common bug. MDN’s example is the name Ben & Jerry's: put into a query and passed through encodeURI, the & stays unencoded (?choice=Ben%20&%20Jerry's), so the server sees a parameter choice with the value Ben and a second, empty parameter called Jerry's. Encoding only the value with encodeURIComponent gives Ben%20%26%20Jerry's, which arrives intact.

The reverse mistake breaks a URL in another way. A full address encoded as a value loses its structure: https://example.com/my file.pdf?q=café&x=1#top becomes https%3A%2F%2Fexample.com%2Fmy%20file.pdf%3Fq%3Dcaf%C3%A9%26x%3D1%23top. That is correct when the whole URL is itself a parameter value, for example in a redirect link like ?next=https%3A%2F%2F.... It is wrong when you want the address to stay a working link. With encodeURI the same input becomes https://example.com/my%20file.pdf?q=caf%C3%A9&x=1#top.

Note that encodeURIComponent keeps ! ' ( ) *. RFC 3986 lists them as reserved sub-delimiters, so MDN shows an extra replacement step for code that must encode them strictly. Most servers accept them either way.

Decoding, and the double-encoding trap

Decoding reverses the process: each %XX becomes its byte, and the bytes are read as UTF-8. The decoder turns https://example.com/search?q=caf%C3%A9+%26+cr%C3%A8me&page=2%2F3 into https://example.com/search?q=café & crème&page=2/3. Notice that the decoded & now looks exactly like the real parameter separator. Decode a whole URL to read it, not to send it on.

RFC 3986 (section 2.4) warns that implementations “must not percent-encode or decode the same string more than once”. Encoding twice is the usual cause of %2520 in links: the % of %20 is itself encoded as %25. A single decode gives back %20, not a space. The encoder warns about this: when the text you paste already contains %XX sequences, its status line says they were encoded again and suggests decoding first. The decoder’s Decode repeatedly (%2520 → %20 → space) option keeps decoding while percent codes remain, up to six passes in total. %25E2%2582%25AC comes out as €. Use it only when you know the text was double-encoded, because it also decodes codes that were meant to stay: text that should read %41, correctly sent as %2541, comes out as A.

Codes that are not valid UTF-8 are kept as written. %E9 alone (how the old Latin-1 encoding wrote é) stays %E9, and the decoder adds a note giving the number of sequences it left unchanged and where they are (line and column). A lone % that is not followed by two hex digits, as in 100%, also stays as it is.

When you need it

  • Building links by hand: a search URL, a mailto: subject, or a tracking parameter with spaces or & in the value. Encode only the value.
  • Reading a messy link: a URL copied from an email or analytics report full of %2F and %3D. Decode it to see where it really points. To pull the links out of a long text first, use Extract URLs.
  • File names on a web server: a file called Price list (2026).pdf needs %20 in its link. A URL slug with only lowercase letters, digits and hyphens avoids the problem. The text to slug converter makes one.
  • APIs and redirects: a URL passed as a parameter of another URL must be encoded as a value, or its own ? and & will mix with the outer ones.

URL encoding is often confused with Base64, which also turns text into safe characters but works on whole blocks of bytes and makes the result about a third longer. What is Base64 explains when to use which.

Questions

What does %20 mean in a URL?

%20 is an encoded space. 20 is the hexadecimal value of the space character (32 in decimal), so a file called my file.pdf appears in a link as my%20file.pdf. Paste the address into the URL decoder to see the plain text.

Is URL encoding the same as encryption?

No. It hides nothing. Anyone can reverse it, and the rules are public. It only makes sure characters such as spaces, & and ? reach the server as data and are not read as part of the URL's structure.

Should I use %20 or + for spaces?

Use %20 in the path of a URL. A + only means a space in the query string of form-style data (application/x-www-form-urlencoded), which is what an HTML form sends with the GET method. In a path, + is a literal plus sign.

Why do I see %2520 in a link?

The text was encoded twice. The first pass turned a space into %20, and the second pass encoded that % as %25, giving %2520. Decode it twice, or tick "Decode repeatedly" in the URL decoder, to get the space back.

Are URLs case-sensitive in their percent codes?

No. RFC 3986 treats %2f and %2F as the same, but it recommends that software produce uppercase hex digits. JavaScript's encodeURIComponent and our encoder output uppercase.

Sources