URL encoder/decoder · Guide
Encode Query String Parameters Without Breaking the URL
Query-string encoding protects parameter keys and values from being mistaken for URL structure. Encode each component, then join the encoded pairs with `=` and `&`; do not encode the entire assembled URL. Using the platform's query builder is safer still because it handles spaces, repeated keys and Unicode consistently, while avoiding the silent truncation caused by raw `&`, `#` or `+` characters.
Encode components, preserve separators
In /search?q=red%20%26%20blue&page=2, ?, =, and & are structural delimiters. Only the key and value data are encoded. Encoding the entire string would turn those delimiters into data and the server would no longer see two parameters.
q = encodeURIComponent('red & blue');
page = encodeURIComponent('2');
const query = `q=${q}&page=${page}`;
// q=red%20%26%20blue&page=2encodeURIComponent is appropriate for one component; encodeURI deliberately preserves &, =, ? and #, so it cannot safely encode an arbitrary value. A value a&admin=true passed through encodeURI creates an extra parameter.
Prefer a builder over interpolation. It determines where structure ends and data begins, making it harder for user input to inject a new key or fragment.
Use URLSearchParams in JavaScript
URLSearchParams accepts raw values and applies form-style query encoding. Set parameters on a URL object when you have a base URL; it preserves existing structure and correctly places the query before the fragment.
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'C++ & Rust');
url.searchParams.set('page', '2');
console.log(url.toString());
// https://example.com/search?q=C%2B%2B+%26+Rust&page=2Spaces become + because URLSearchParams serialises using form rules. Literal plus signs become %2B, so C++ round-trips correctly. Do not replace %2B with +; a form-style parser would decode those plus signs as spaces.
Calling append creates repeated keys while set replaces existing values. That difference matters for filters and checkboxes; choose the shape the receiving API documents.
Build queries in Python and PHP
Python's urlencode and PHP's http_build_query encode mappings and sequences without destroying separators. Both default to form-style spaces unless configured otherwise. Pass raw values rather than values already processed by quote or urlencode.
from urllib.parse import urlencode
query = urlencode({'q': 'C++ & Rust', 'page': 2})
print(query)
# q=C%2B%2B+%26+Rust&page=2$query = http_build_query(
['q' => 'C++ & Rust', 'page' => 2],
'',
'&',
PHP_QUERY_RFC3986
);
// q=C%2B%2B%20%26%20Rust&page=2PHP's RFC 3986 mode uses %20 for spaces; its default form encoding uses +. Both usually decode correctly in queries, but signatures and strict receivers may prescribe one canonical representation. Match the protocol rather than changing style after signing.
Spaces and plus signs are not interchangeable data
A percent-encoded space is %20. In form-style queries a plus also represents a space, while a literal plus must be %2B. This distinction silently corrupts phone numbers, time-zone offsets and email aliases when a value is concatenated without encoding.
raw value encoded query value
hello world hello%20world or hello+world
+380 50 123 %2B380%2050%20123
user+tag@test user%2Btag%40test
a&b a%26bDo not decode by replacing plus with space and then calling a decoder unless you are deliberately implementing form decoding. Standard query parsers already apply the right order and handle malformed percent sequences more predictably.
If a signed URL fails only when values contain spaces, compare the exact canonical byte string. %20 and + may mean the same decoded value but produce different signatures.
Test these characters as a small interoperability suite: a space, a literal plus, an ampersand, an equals sign, a hash and non-ASCII text. Each reveals a different assumption before production data does.
Arrays, repeated keys and empty values
There is no single universal array syntax. APIs may use repeated keys (tag=a&tag=b), brackets (tag[]=a), indexed brackets or a comma-separated value. Encoding cannot choose the contract; consult the receiver and generate that shape deliberately.
const p = new URLSearchParams();
p.append('tag', 'red');
p.append('tag', 'blue');
p.append('empty', '');
console.log(p.toString()); // tag=red&tag=blue&empty=
console.log(p.getAll('tag')); // ['red', 'blue']An empty value flag= differs from an absent key, and some parsers distinguish a key without = as well. Do not remove empty pairs during cleanup unless the API declares them equivalent.
Object-to-string coercion is another silent failure: appending an array or object directly may yield comma-joined text or [object Object]. Serialise to the server's documented shape before adding it.
Unicode and canonical representation
Query encoding first converts text to UTF-8, then percent-encodes bytes that are not safe. One visible character may become several %XX groups. Do not percent-encode UTF-16 code units or a legacy system code page.
const p = new URLSearchParams({ q: 'café ☕' });
console.log(p.toString());
// q=caf%C3%A9+%E2%98%95
console.log(new URLSearchParams(p).get('q'));
// café ☕Visually identical Unicode strings can have different code-point sequences. Most applications should preserve user input, but protocols that sign or compare exact query strings may require Unicode normalisation before UTF-8 encoding. Apply only the form required by that protocol.
Hex digits in percent escapes are case-insensitive, yet canonical signing rules may require uppercase. Let the signing library build the canonical query rather than post-processing an ordinary URL.
Detect double encoding and malformed input
Encoding an already encoded value changes %20 to %2520; one decode then yields literal %20, not a space. Unexpected %25 is the clearest diagnostic. Decide which layer owns encoding and pass raw data to it exactly once.
const raw = 'a b';
const once = encodeURIComponent(raw); // a%20b
const twice = encodeURIComponent(once); // a%2520b
decodeURIComponent(twice); // a%20bDo not blindly decode until a string stops changing. Data may legitimately contain percent sequences, and repeated decoding can turn encoded delimiters into structure or enable traversal payloads. Parse the URL once, then validate the decoded value according to its business meaning.
Malformed escapes such as % or %G0 may throw or be preserved depending on the library. Reject malformed externally supplied URLs at a clear boundary instead of letting different services decode them differently.
Frequently asked questions
How do I encode query string parameters?
Give raw keys and values to a standard builder such as URLSearchParams, Python urlencode, or PHP http_build_query. It encodes each component while preserving the =, & and ? separators that form the URL.
Should I encode the whole query string?
No. Encoding the assembled string turns separators into data, so the server no longer sees separate parameters. Encode individual keys and values, or let a query builder assemble them.
Should a query-string space be + or %20?
Both commonly decode as a space in query strings. %20 is standard percent-encoding; + comes from form encoding. Follow the receiver's canonical rule for signed requests, and encode a literal plus as %2B.
Why does my query contain %2520?
The value was encoded twice: the percent sign in %20 became %25. Keep values raw until one designated URL-building layer encodes them.
How do I encode multiple values for one parameter?
Use the convention documented by the API. Repeated keys such as tag=red&tag=blue are widely supported, while bracket and comma-separated conventions are framework-specific.
Does URLSearchParams encode ampersands in values?
Yes. A literal ampersand in a value becomes %26, while the ampersands it generates between pairs remain separators. It also encodes literal plus signs as %2B.
Ready to try it?
Open the free browser-based URL encoder/decoder and apply what you just read — no sign-up, runs locally.
Open the URL encoder/decoder tool