URLs, Encoding, and Query Strings
Percent-Encoding, Base64, and How the Web Passes Data
12 min read • Updated March 2026
Anatomy of a URL
Every URL (Uniform Resource Locator) has a defined structure governed by RFC 3986. Understanding this structure helps make sense of why encoding is necessary and where it applies.
URL breakdown:
https://example.com:8080/path/to/page?name=John+Doe&lang=en#section-2 Scheme: https — the protocol
Host: example.com — the domain
Port: :8080 — optional, defaults to 80 (HTTP) or 443 (HTTPS)
Path: /path/to/page — the resource location
Query string: ?name=John+Doe&lang=en — key-value parameters
Fragment: #section-2 — anchor within the page (never sent to server)
Each component has its own rules about which characters are allowed unencoded. The rules differ between the path, query string, and fragment — which is why URL encoding can be confusing.
Why URLs Need Encoding
URLs can only contain a limited set of ASCII characters. The characters A-Z, a-z, 0-9, and a handful of special characters (-, _, ., ~) are "unreserved" — they can appear in URLs without any encoding.
Other ASCII characters like ?, &, =, /, and # are "reserved" — they have special meaning within the URL structure. If you want to pass a value that contains one of these characters (like a search query containing an ampersand), you must encode it so the server doesn't misinterpret it as a URL delimiter.
Non-ASCII characters — anything outside the basic ASCII range, including letters from non-Latin alphabets, emoji, and accented characters — must always be encoded because they're not part of the original URL specification.
Example of why encoding matters:
Intended: Search for "cats & dogs"
❌ /search?q=cats & dogs Server sees: q="cats " (parameter ends at &), then "dogs" as unknown second parameter
✓ /search?q=cats%20%26%20dogs Server correctly receives: q="cats & dogs"
Percent-Encoding Explained
Percent-encoding (also called URL encoding) represents unsafe characters using a percent sign followed by two hexadecimal digits. The hexadecimal value is the UTF-8 byte value of the character.
For example, the space character has the UTF-8 byte value of 32 decimal, which is 20 in hexadecimal — so a space becomes %20. The ampersand (&) has the byte value 38 decimal = 26 hexadecimal, so it becomes %26.
Common percent-encoded characters:
Notice that the Euro sign (€) encodes to three percent-encoded bytes (%E2%82%AC). This is because the Euro sign requires 3 bytes in UTF-8 encoding, so each byte gets its own %XX representation.
The + Confusion
In query strings specifically, a + sign is historically used to represent a space (inherited from HTML form encoding). So q=hello+world and q=hello%20world both mean "hello world" in query strings. However, + outside of query strings is not a space — it's a literal plus sign. This inconsistency causes bugs; using %20 for spaces is unambiguous in all contexts.
Query Strings and Parameters
Query strings are the mechanism for passing parameters to a web server through the URL. They begin with ? and consist of key-value pairs separated by &.
Query string anatomy:
?category=electronics&sort=price_asc&page=2&q=USB+cable category=electronics — first parameter
sort=price_asc — underscore doesn't need encoding
page=2 — numbers don't need encoding
q=USB+cable — space encoded as +
Reading Query Parameters
In JavaScript, the URLSearchParams API provides a clean way to parse and manipulate query strings:
const params = new URLSearchParams(window.location.search);
const query = params.get('q'); // "USB cable" (already decoded)
const page = params.get('page'); // "2"
const sort = params.get('sort'); // "price_asc"
// Setting parameters
params.set('page', '3');
const newUrl = '?' + params.toString();
// Automatically encodes values properlyURLSearchParams handles all the encoding and decoding automatically — you work with the decoded values and it handles the URL-safe representation.
URL Encoding in JavaScript
JavaScript provides three ways to encode URLs, and choosing the wrong one is a common source of bugs:
encodeURIComponent()
Encodes a value to be used as a component of a URL (like a query parameter value or path segment). It encodes everything except letters, digits, -, _, ., !, ~, *, ', (, ). Use this when encoding individual values to be inserted into a URL.
encodeURIComponent('cats & dogs') // → "cats%20%26%20dogs"encodeURI()
Encodes a complete URL, preserving URL-structural characters like ?, &, =, /, :, and #. Does NOT encode these structural characters because they're assumed to be intentional parts of the URL structure. Use this when you have a complete URL and only need to encode unsafe characters like spaces or non-ASCII chars.
encodeURI('https://example.com/path with spaces') // → "https://example.com/path%20with%20spaces"escape() — Deprecated
An old function that should not be used for URL encoding. It uses non-standard encoding for non-ASCII characters and doesn't handle Unicode correctly. Use encodeURIComponent() or encodeURI() instead.
Rule of thumb: When building URLs programmatically, always encode individual parameter values with encodeURIComponent() or use the URLSearchParams API which handles encoding automatically.
What Is Base64?
Base64 is a binary-to-text encoding scheme that represents binary data using only printable ASCII characters. Despite the name, it's not technically URL encoding — but it's frequently used in web contexts and often confused with URL encoding.
Base64 works by taking groups of 3 bytes (24 bits) of binary data and encoding them as 4 ASCII characters from a 64-character alphabet consisting of letters (A-Z, a-z), digits (0-9), and + and /. The encoded output is always 33% larger than the input.
Base64 encoding example:
Input: "Hello, World!" (13 bytes)
Base64: SGVsbG8sIFdvcmxkIQ== (20 characters)
The == is padding added when the input length isn't divisible by 3
Common Uses of Base64
- Email attachments: MIME encoding uses Base64 to embed binary files in text-based email messages
- Data URLs: Embedding images directly in CSS or HTML using
data:image/png;base64,... - JSON web tokens (JWTs): JWT claims are Base64url-encoded (a URL-safe variant)
- API authentication: HTTP Basic Authentication sends credentials as Base64-encoded strings
- Storing binary data in text fields: Databases or APIs that only accept text strings can store binary via Base64
Base64 vs. Base64url
Standard Base64 uses + and / which are special characters in URLs. "Base64url" replaces these with - and _, making the encoded string safe to use in URLs without percent-encoding. JWTs use Base64url encoding specifically for this reason.
URL Encoding vs. Base64 — When to Use Which
These two encoding schemes serve different purposes and are commonly confused. Here's a clear decision guide:
Use Percent-Encoding when:
- • Including user input in a URL (search queries, filter values)
- • Building URLs programmatically in JavaScript
- • Encoding form data for GET requests
- • Any value that will be part of a URL structure
Use Base64 when:
- • Embedding binary data (images, files) in text-based formats
- • Working with JWTs or OAuth tokens
- • HTTP Basic Authentication headers
- • Embedding small images as data URLs
Neither is encryption
Both URL encoding and Base64 are encodings, not encryption. They are trivially reversible and provide zero security. Never use Base64 to "hide" sensitive data — it provides no protection. For sensitive data, use proper encryption.
Encoding and Decoding Tools
These browser-based tools let you encode and decode values instantly:
URL Encoder / Decoder
Percent-encode and decode URL strings instantly in your browser.
Base64 Encoder / Decoder
Encode and decode Base64 strings and binary data.
JWT Decoder
Decode and inspect JSON Web Tokens (Base64url encoded).
QR Code Generator
Generate QR codes from URLs — see how URLs are embedded in encoded form.