Desk Clarity

A field guide to everyday digital problems.

Developer tools / Practical guide

An Ampersand Splits a Query Value: Encode Components at the URL Boundary

Encode an individual query value as a component, or build the query with URLSearchParams.

By ON Lab2 min readSources checked November 10, 2026 (KST)

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%2Fnotes

Why 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.

Sources & further reading