Micro Tool Yard logo
Tools

Blog

Why URLs Can Only Use About 20 Characters Safely

A URL's own syntax reserves a handful of characters as structural punctuation — percent-encoding is the escape hatch that lets everything else, including those same characters as literal data, travel inside one.

A URL is a tiny programming language: characters like :, /, ?, &, and # aren't just punctuation, they're syntax that separates the scheme, host, path, query string, and fragment. That's precisely the problem the moment you need one of those characters, or a space, or non-ASCII text, to appear as literal data rather than structure — the URL spec can't tell the difference unless you escape it first.

Percent-encoding: one byte, three characters

The escape mechanism is simple by design: any byte can be represented as % followed by its two-digit hexadecimal value, so a space (byte 0x20) becomes %20 and an ampersand (byte 0x26) becomes %26. Because it operates on raw bytes rather than characters, it also handles non-ASCII text correctly once that text is first converted to UTF-8 — an emoji or an accented letter just becomes a longer sequence of %XX escapes, one per UTF-8 byte, with no separate encoding scheme needed.

Why there are two different encoding functions

This is also exactly why encoding tools (and browsers' own encodeURIComponent versus encodeURI) draw a line between "a value going inside a URL" and "an entire URL." A single value — a search term, a redirect target, a filename — has no business containing unescaped &, =, or /, since those would be misread as query-string syntax rather than as part of the value; encoding it fully (the "Component" behavior) is always correct. A complete URL, on the other hand, is supposed to contain those characters as structure — escaping them would break the URL rather than protect it — so encoding a full URL only needs to escape genuinely unsafe characters like spaces, leaving the delimiters alone.

The bug this distinction prevents

Conflating the two is a recurring source of real bugs. Encode an entire URL with the "component" rules and its own :// and ? get escaped into unusable garbage the browser can no longer parse as a URL at all. Go the other direction — encode a single value meant for a query string using the "full URL" rules — and an & or = inside that value survives unescaped, silently injecting an extra parameter into the query string it's supposed to be a single value within. Both failures look identical from the outside (a broken or hijacked link) but come from picking the wrong one of two similar-looking encoding functions.

Decoding is the same escape hatch in reverse

Decoding just reverses the substitution — each %XX triplet becomes its original byte, and consecutive UTF-8 continuation bytes get reassembled back into the original character. It fails loudly (rather than guessing) when the input isn't actually percent-encoded, or when a UTF-8 byte sequence is truncated — both are useful signals that the text you're decoding didn't come from a matching encode step, which is exactly the kind of mismatch an encode/decode tool is built to catch quickly rather than silently producing mojibake.