Notes › Tech Notes › Chinese in URLs turns into %E4%BA%86: URL encoding and decoding

Chinese in URLs turns into %E4%BA%86: URL encoding and decoding

An ops teammate once posted a campaign landing page link in our group chat and said the search term was empty when she opened it on her phone. The link looked like this: ?keyword=kids backpack&from=wx. Copying and pasting it on my own machine worked fine, but tapping it after it went through WeChat broke it. It took me half an hour to pin down two problems. First, the space was replaced with + somewhere along the transport chain, and the back end decoded by the %20 rule, so the first character of the search term became a plus sign. Second, the link had the entity & written as a bare &, and some clients swallow everything after it as an entity. A link looks like just a string of characters, but it is one of the earliest strictly constrained formats: any character outside the allowed set has to become %XX, and that is percent encoding.

1. Why a URL cannot hold whatever you like

The skeleton of a URL accepts only a limited set of ASCII characters. Chinese text, spaces and quotation marks are things that must be escaped. Symbols such as : / ? & = and # already have a job in the grammar: ? starts the query string, & separates parameters, = separates key from value and # starts the fragment. If these characters appear inside a parameter value without escaping, the parser splits your value again according to the grammar. The Chinese character for middle takes three bytes in UTF-8, E4 B8 AD, and putting a percent sign before each byte gives %E4%B8%AD, so the gibberish you see in a link is really three bytes.

Decoding works the same way: given %E4%B8%AD%E5%9B%BD, split on the percent signs into bytes and assemble them as UTF-8, and you get the Chinese word for China. There is no ambiguity here. The only variable is which encoding rule was used at the time.

2. Three rules and how they differ: spaces, and who owns / ? & =

MethodSpace becomesHandling of : / ? & = #Typical useCommon mistake
encodeURI%20Kept as structureThe whole link is already assembled and you only want to escape illegal characters such as Chinese text and spacesUsing it on a parameter value leaves the & inside the value intact and splits off the parameters that follow
encodeURIComponent%20Escaped as wellBuilding parameter values, the most common agreement between front end and back endUsing it on a whole link escapes the :/ in the scheme and the link stops working
PHP urlencode+Escaped as well, except - . _Working with a PHP back end, legacy form submissionMixing it with a side that follows the %20 convention, so spaces and plus signs bleed into each other

All three turn a literal % into %25, which is exactly where double encoding comes from: the value was already encoded once, then some layer encoded the whole query string again, so %E4 becomes %25E4, and one round of decoding only returns the string %E4 rather than a character. The test is simple: if %25 shows up in the result, it was encoded twice. Also keep query strings and paths apart. Form encoding, application/x-www-form-urlencoded, specifies that a space is written as +, while in a path + is just a plus sign and a space must be %20. Mix the two rules and oddities appear, such as a search term whose first character turns into a plus sign, because one side decodes with + while the other encodes with %20.

3. The order I follow every time

  1. Read the symptom first: is the whole value garbled, does only the space fail, or is the parameter cut off at some position. A cut-off is almost always an unescaped & or #.
  2. Capture both the original string that fails and the value the back end receives, from the browser address bar, a proxy log or the framework request object, then confirm whether the difference happens while encoding or while decoding.
  3. Decode it with all three modes in URL Encoder and Decoder and see which one yields readable text. The mode that decodes correctly is the rule that was actually used. This step is far faster than guessing at back-end settings.
  4. Fix it with one convention: always build parameter values with encodeURIComponent, and use encodeURI only as a fallback escape for a whole link. Write down in the API docs whether a space is %20 or +.

If the content is going into HTML or a JS string rather than a URL, that is a different escape system: a form such as \u4e2d belongs to Unicode Converter. The two results are not interchangeable, which is another place where investigations get crossed.

4. A few edges that are easy to miss

  • Non-ASCII text in a path: the browser address bar shows ordinary characters, but the request that goes out already carries encoded bytes. When the link is copied into a chat app, some clients encode it again, so the other side receives the %25 version.
  • Mismatched character sets: if the server decodes as GBK while the content is UTF-8, the output is mojibake instead of the original text. Switching modes will not help; the two ends have to agree on a character set first.
  • Encoding something that is already an encoded string: when a redirect link contains a nested URL, ?target=https://...?a=1&b=2, the inner link must go through encodeURIComponent, otherwise &b=2 is eaten by the outer level as one of its own parameters.
  • SEO: search engines understand non-ASCII URLs, but they display them as encoded strings, which read poorly in shares and logs. English or pinyin slugs are easier to live with.
  • Encoding is not filtering: URL encoding only converts a transport format and performs no security check. Parameters still need strict validation by type.

5. Common questions

Q: %20 and + both mean a space, so can they be swapped freely?

No. Inside a query string many implementations accept both, but form encoding (x-www-form-urlencoded) specifies + for a space, while a path requires %20. Mixing them produces errors in both directions, a search term starting with a plus sign or a plus sign turning into a space.

Q: Why does one Chinese character take three percent segments?

Because percent encoding works byte by byte, and a common Chinese character occupies three bytes in UTF-8. Each byte is written as %XX, so it becomes nine characters. This also gives a rough way to guess the character set: three segments usually means UTF-8, two usually means GBK.

Q: What if the decoded result is still garbled?

Check in order. First confirm it is not double encoded, which is the case when %25 appears in the result. Then try another decoding mode. Finally verify that both ends use the same character set. Decoding with all three modes costs almost nothing.

Q: Can Chinese text go directly into a URL?

Modern browsers encode it automatically when you press Enter, so yes, you can type it. But server logs, packet capture tools and anything crossing systems will see the encoded form, so when writing docs and example links it is better to show the encoded version and avoid ambiguity.

Q: What happens if a parameter value contains #?

Nothing after # is sent to the server; the browser treats it as a fragment identifier. This is the most commonly overlooked cause when a parameter mysteriously disappears.

Looking back at that empty search term on the phone, neither cause was in the business logic. Both came down to where the space and the & belong along the transport chain. You do not need to memorize every encoding rule: grab the original string that fails, decode it with all three modes, and the answer usually shows up on screen.

📎 Related articles

PNG compressed three times only lost 426 bytes: the real boundary of lossless compression 2026-10-22 How Many Times Bigger a PNG Gets After Converting From WebP: When It Is Unavoidable and How to Keep the Growth Down 2026-10-22 Three Reasons the Small Text Smudges When a Screenshot Is Shrunk to a Quarter 2026-10-22 Cropped to the Same 300x300, Why Does the Avatar Always Sit Half a Cell Low-Right 2026-10-22 Forecasting 18 months at 1.7 times a month: why the script and the sheet give two numbers 2026-10-22