A string containing an ampersand can be either a query value or part of a URL’s structure. No encoder can infer which meaning you intended. Choose the boundary before encoding, then keep that boundary consistent when decoding.
For one value, encode the component
Suppose a search phrase is café & tea. It belongs inside a single query parameter. Component encoding protects the ampersand and spaces so they cannot become a second parameter or invalid URL whitespace.
Input: café & tea
Operation: Encode
Boundary: Component
Expected: caf%C3%A9%20%26%20tea
Whole URL encoding preserves structural characters such as /, ?, &, = and #. Encoding the complete string https://api.example.test/search?q=café & tea therefore leaves the ampersand as a separator. It cannot know that this particular ampersand was supposed to be part of the phrase.
Whole URL input:
https://api.example.test/search?q=café & tea
Expected encodeURI output:
https://api.example.test/search?q=caf%C3%A9%20&%20tea
A plus sign has context
In form-style query parsing, + represents a space. The literal plus must be encoded as %2B. In standalone component decoding, decodeURIComponent does not turn + into a space. The URL Workbench follows these distinct rules rather than applying one decoder everywhere.
Query: ?q=a+b&q=a%2Bb
Expected ordered parameters:
1. q = a b
2. q = a+b
Repeated keys remain separate entries in source order. Converting query parameters into a plain object would lose this information, so the Workbench returns an ordered list of key/value pairs. An empty value is different from a missing key; both are visible in the parser output.
Decode once, at the right boundary
Encoding an existing percent sign produces %25. Decoding twice can turn data into syntax unexpectedly. Track whether a value is raw text or already encoded rather than adding encode/decode calls until an output looks plausible. In whole-URL decoding, reserved separators such as %26 stay escaped; component decoding turns %26 into an ampersand.
decodeURIComponent("%E2%28%A1")
Expected: malformed UTF-8 error; input preserved.
Parsing is local and never visits the URL. The HTTP(S) parser uses browser URL normalization, which may canonicalize a host or remove a default port. Path and fragment fields remain encoded so their boundaries stay visible. Use the encoding operation separately when you intend to transform a component.