URL encoding vs form encoding
Published: 2026-05-27
How percent-encoding works in URLs, when encodeURI differs from encodeURIComponent, and why application/x-www-form-urlencoded treats spaces as plus signs.
URL encoding (more precisely percent-encoding) turns bytes that are not allowed in a URL part into %HH sequences so links stay valid. Form encoding (application/x-www-form-urlencoded) reuses that idea for HTML forms and many HTTP APIs, but with extra conventions—most famously spaces as + instead of %20. Mixing the two rules is a common source of “it works in Postman but breaks in the browser” bugs.
Percent-encoding in one minute
Only a subset of ASCII characters can appear literally in every URL context. Everything else is encoded as % + two hexadecimal digits for the byte value (UTF-8 bytes for non-ASCII text).
Examples:
| Character | Encoded (UTF-8) |
|---|---|
| space | %20 |
& |
%26 |
= |
%3D |
é (U+00E9) |
%C3%A9 |
Decoding is the inverse: scan for %, read two hex digits, emit the byte, then interpret the byte sequence as UTF-8 (or the charset the application expects).
Reserved characters (:, /, ?, #, [, ], @, !, $, &, ', (, ), *, +, ,, ;, =) mean different things depending on where in the URL the value sits—path, query, or fragment—so encoders must know which part you are filling in.
Where encoding applies in a URL
A typical HTTPS URL has distinct zones:
https://user:[email protected]:8080/path/to/page?name=hello%20world&tag=a%2Bb#section-2
|-------| |-------------------| |----------| |------------------------| |---------|
origin authority pathname search (query) hash
Practical rule of thumb:
- Encode individual values (query parameter values, path segments, fragment identifiers) before you assemble them into the full string.
- Do not percent-encode an entire URL after it is already valid—structural characters like
://and?must stay unescaped or the link breaks.
encodeURI vs encodeURIComponent (JavaScript)
Browsers expose two related helpers (other languages have equivalents):
| API | Typical use | Keeps unescaped (examples) |
|---|---|---|
encodeURI |
A mostly complete URL string | :, /, ?, # (structure) |
encodeURIComponent |
A single component: query value, path segment, fragment | Almost nothing except A–Z a–z 0–9 - _ . ! ~ * ' ( ) |
Examples with the string hello world/a?b:
encodeURIComponent('hello world/a?b')→hello%20world%2Fa%3Fb(slashes and?are escaped—correct for one value).encodeURI('https://ex.com/a b')→https://ex.com/a%20b(scheme and slashes stay readable).
Use encodeURIComponent (or your language’s “component” encoder) when building ?key=value pairs. Use encodeURI only when you need to escape a longer URL-like string but still preserve delimiters.
Form encoding (application/x-www-form-urlencoded)
HTML forms and many REST clients submit bodies (or sometimes queries) as key=value&key2=value2:
- Keys and values are encoded like URI components (reserved characters become
%HH). - Spaces are often written as
+instead of%20. Both decode to space in this encoding—+is not a literal plus sign unless it is percent-encoded as%2B. - The
Content-Typeheader isapplication/x-www-form-urlencoded(often withcharset=UTF-8).
The same “plus means space” rule appears in query strings on GET forms, which is why ?q=hello+world is common even though %20 is also valid.
When decoding, you usually:
- Split on
&into pairs, then on the first=into key and value. - Replace
+with space if you are in form/query mode. - Run percent-decoding on each key and value.
When encoding, pick one space style and stay consistent: APIs differ; some expect %20, some accept +.
Side-by-side: URL component vs form style
| Topic | URI component encoding | Form / query (x-www-form-urlencoded) |
|---|---|---|
| Space in a value | %20 (typical) |
+ or %20 (both decode to space) |
Literal + in a value |
%2B |
Must be %2B or it becomes space on decode |
&, = in a value |
Must be %26, %3D |
Same—otherwise you break pair splitting |
| Non-ASCII | UTF-8 bytes as %HH |
Same |
| Where it shows up | Path segments, fragments, any isolated value | HTML POST bodies, many OAuth/token endpoints, search ?q= |
Neither encoding encrypts or signs data. Anyone can decode percent sequences; never put secrets in URLs assuming encoding hides them.
Common pitfalls
- Double encoding: Running
encodeURIComponenton text that already contains%20turns%into%25, producing%2520. Decode once, encode once at the boundary. - Encoding the whole URL: Turning
https://intohttps%3A%2F%2Fbreaks navigation. Encode parts, then concatenate. - Decoding too early: Middleware that decodes query values before your app runs can change
+and%semantics. Log the raw string when debugging. - Plus vs space: A password or base64 token that includes
+must be sent as%2Bin form contexts, or it may arrive as spaces. - Assuming
decodeURIfixes form data: Use component decoding (decodeURIComponent) per value, and apply+→ space only when the source is form-encoded. - Path vs query: A slash inside a single path segment must be
%2F; a slash between segments is a delimiter and stays/.
JSON and APIs (brief)
JSON request bodies (application/json) do not use form encoding—strings are quoted in JSON, and HTTP libraries handle the body as UTF-8 text. You still URL-encode values when those same fields appear in query strings or path parameters on the same API. Mixed styles on one service are common (JSON body + form-encoded token endpoint).
Try it locally in your browser
Use the URL Encoder, Decoder & URL Parser to percent-encode or decode text with URIComponent vs URI rules, toggle treat + as space when decoding form-style values, and break a full URL into protocol, host, path, query, and hash—all without uploading your strings.
If you are escaping text for HTML—not for URLs—see the HTML Entity Escape & Unescape tool; entity encoding solves a different problem (markup safety, not URL structure).
Related reading
.envfiles: syntax, merging, and secrets hygiene — configuration values that often end up in URLs or headers; encoding does not replace secret handling.- What is JSON? — when APIs use JSON bodies instead of form fields.