Encode an individual query value as a component, or build the query with URLSearchParams. encodeURI preserves URL separators and therefore cannot safely protect arbitrary data inserted into one value.
Reproduce the behavior safely
Run this self-contained fixture in an empty temporary directory, separate from your project. Shell examples create only named fixture files there. JavaScript examples use local values and send no network requests.
const assert = require('node:assert/strict');
const term = 'tea & cake/notes';
const wrong = new URL('https://example.invalid/?q=' + encodeURI(term));
assert.notEqual(wrong.searchParams.get('q'), term);
const explicit = new URL('https://example.invalid/?q=' + encodeURIComponent(term));
assert.equal(explicit.searchParams.get('q'), term);
const built = new URL('https://example.invalid/');
built.searchParams.set('q', term);
assert.equal(built.searchParams.get('q'), term);
console.log(built.href); // No request is sent.Observed local output
https://example.invalid/?q=tea+%26+cake%2FnotesWhy the result changes
The ampersand belongs to the search term but also has structural meaning in a query. encodeURI leaves that separator available, so the assembled URL can describe another parameter instead of the intended text. The fixture compares the decoded value through a URL parser instead of relying on whether the address looks plausible.
Choose and verify the correction
Identify whether you are handling a full URL, path segment or query value. For queries, create a URL and set each parameter from raw data. If manual assembly is required, encode each value once with the appropriate component operation. Parse the result and assert that every decoded parameter equals the original input, including ampersands, hashes, plus signs and non-ASCII text. Keep untrusted data out of structural template positions.
Limits and next checks
The fixture only constructs local URL objects. Encoding is not destination authorization, HTML escaping or a general security validator. Do not pre-encode a value and then pass it to searchParams, which would encode the percent signs again. A slash inside a path segment may have server-specific routing consequences even when encoded. The related guide covers the separate plus-as-space query parsing issue.
Related: URLSearchParams Changes a Base64 Plus to a Space: Serialize the Raw Value Once.
Isolated fixture executed on macOS on November 10, 2026 (KST), with v26.5.0. The observed output verifies this example, not every application or runtime.