Back to Updates
DevTool Team

Choosing URL Parameter Encoding: component, URI, and form Modes

Chinese characters breaking your URL parameters? Ampersands truncating them? The problem isn't the characters — it's picking the wrong encoding mode. This guide routes by what you're encoding: parameter values take encodeURIComponent (& = ? # all escaped), whole URLs take encodeURI (query structure preserved), form bodies take the form mode (spaces become +). It explains where %E4%BD%A0 comes from (three UTF-8 bytes in percent notation), the + versus %20 saga (how the form-encoding legacy manufactures signature-verification failures), and troubleshooting double encoding and Invalid URI errors — ending with the truth: encoding solves transport safety, not parameter semantics.

Route first: what are you encoding

Three different jobs hide behind "URL encoding", and picking the wrong mode is how accidents start:

What you're encoding The mode Why
A parameter value (content going after ?key=) Component (encodeURIComponent) The & = ? # inside the value must be escaped, or & gets parsed as the next parameter's separator
A complete URL (already has ? and & structure; just legalize spaces and Chinese) URI (encodeURI) Structure characters preserved; only illegal characters encoded
A form submission body (application/x-www-form-urlencoded) Form mode Spaces encode as + instead of %20 — a legacy convention, more below

All three modes (the URL Encoder Decoder supports them; switch and compare):

input:      a=hello world&next=你
component:  a%3Dhello%20world%26next%3D%E4%BD%A0   (& = escaped — treated as data)
uri:        a=hello%20world&next=%E4%BD%A0          (structure kept, only illegal chars encoded)
form:       a%3Dhello+world%26next%3D%E4%BD%A0      (spaces become +, form convention)

One-line memory hook: encoding "data" takes component, encoding an "address" takes uri, going through a "form" takes form.

Why parameters must be encoded

Skipping encoding isn't "sometimes works" — it's a buried mine. Four accident scenes:

  • & truncates parameters: an & inside a value reads as the next parameter's start — q=a&b arrives as two parameters, the first one cut;
  • # cuts the query: everything after # is a fragment identifier and never reaches the server — a # in the value makes the rest vanish;
  • Spaces and Chinese: some browsers encode them as a fallback, but "some" means unpredictable behavior that differs across clients;
  • % itself: a raw % in the value becomes an escape introducer on the decoding side, and %GG throws immediately.

The spec's attitude is simple: only a subset of ASCII is guaranteed safe in a URL; everything else is percent-encoded. %E4%BD%A0 is "你" — UTF-8 bytes E4 BD A0, each written as % plus two hex digits. Not mojibake, not encryption — a legal spelling of the same content. One Chinese character mapping to three %XX groups is the UTF-8 signature.

+ versus %20: two faces of a legacy

Spaces have two encodings: %20 (percent-encoding, the standards path) and + (form encoding, an HTML-era convention that stuck).

The accident's typical shape: a value containing a literal + — phone number +86, formula a+b — gets decoded by the server under form rules, and + returns as a space: +8613800000000 becomes 8613800000000. Sneakier still is the signature case: the client encodes spaces as %20, the server normalizes under form rules to +, the signature strings diverge, verification fails — you suspect the secret key, but it's spaces fighting.

Diagnosis: check the request's Content-Type — application/x-www-form-urlencoded means the + convention; JSON bodies leave spaces alone. Fix: encode literal + as %2B on the sending side; standardize both ends on one space representation. A tool with a form mode lets you verify both ends behave consistently.

Troubleshooting checklist: decode failures and double encoding

  • Invalid URI error: anything after % that isn't two hex digits (%GG, a lone %, a truncated %E4%BD) makes decodeURIComponent throw. Search for lone % to detect truncation first;
  • Double encoding: % encoded to %25, so %E4 becomes %25E4 — a string starting with %25 deserves one more decode layer. The cause is usually a framework encoding once and the code encoding again;
  • Still mojibake after one decode: count the %XX groups per Chinese character — three groups is normal UTF-8; remaining garbage means another encoding layer (Base64 inside URL, for instance — strip layer by layer);
  • URL structure broke after decoding: decoding a whole URL in component mode restores & = as data and wrecks the parameter structure — the decoding mode must match how it was encoded.

The truth: encoding solves transport, not semantics

Percent-encoding guarantees "this content can cross the URL channel safely". It does not solve parameter semantics: duplicate keys (which a=1&a=2 wins is the server's business), array serialization formats (a[]=1 vs a=1,2 vs a=1&a=2 are framework conventions), representations of empty and null — no amount of encoding-tool fiddling answers those; check your framework's docs. The tool's territory is the encoding layer's debugging: getting this string encoded right, decoded right, and seen for what it is.

Frequently Asked Questions

What's the difference between encodeURIComponent and encodeURI, and when do I use each?

The difference is their attitude toward URL structure characters: encodeURIComponent escapes & = ? # / entirely — it assumes you're encoding "a piece of data"; encodeURI preserves those characters — it assumes you're encoding "a complete URL". Encoding a parameter value must use encodeURIComponent, or the & inside your value becomes the server's next parameter separator; encoding a fully structured URL is encodeURI's job. Reversed: the former cuts the URL in half, the latter truncates your value.

Why did the Chinese in my URL parameter turn into something like %E4%BD%A0?

That's percent-encoding: the URL specification allows only a subset of ASCII to appear literally. The character first becomes UTF-8 bytes — "你" is E4 BD A0 — and each byte is written as a percent sign plus two hex digits. It's neither encryption nor corruption, just another legal spelling of the same content; a decoder restores it in one step. The tell for UTF-8: one Chinese character maps to three %XX groups.

Why did my plus signs turn into spaces after decoding?

It's the legacy of form encoding: HTML forms encode spaces as + instead of %20, and the convention stuck. A value containing a literal + (phone number +86) gets decoded under form rules, and + returns as a space — signature mismatches and corrupted data trace back here. Check the Content-Type: x-www-form-urlencoded means the + convention applies; JSON bodies don't. The fix: encode literal + as %2B, or standardize both ends on %20.

Why does decoding throw an Invalid URI error?

decodeURIComponent throws on illegal percent sequences — anything after % that isn't two hex digits (%GG, a lone %, a truncated sequence). Three common causes: wrong layer count (double-encoded content decoded only once), a raw % character never encoded to %25, or a string truncated during copy-paste. Troubleshoot in order: search for lone % to detect truncation, then decode once more to detect double encoding.