A single unescaped & can eat half your form data
Build this link by hand:
https://example.com/search?name=Ben & Jerry&flavor=mint
You meant to pass a name of "Ben & Jerry" and a flavor of "mint." The server reads something else. It splits the query string on every &, so it sees three fields:
name=Ben(trailing space and all)Jerryโ a parameter named " Jerry" with no valueflavor=mint
"Jerry" is gone from the name. It got promoted to a key by an ampersand that was never meant to be a separator. The spaces make it worse: raw spaces aren't allowed in a URL, so they get encoded, dropped, or mangled depending on what's handling the string.
The fix is percent-encoding. The & inside the value becomes %26 and each space becomes %20, so Ben%20%26%20Jerry reads as one string. That escaping is the rule that keeps a hand-built URL from silently corrupting your data.
Why URLs can't hold just any character
A URL has structure, and specific characters carry it. The ? starts the query, & separates pairs, = splits a key from its value, # starts the fragment, and / separates path segments. Those characters are load-bearing. If your data contains one, the parser has no way to tell your literal & from a separator &.
Percent-encoding is the escape hatch. Take the character's byte value, write it in hexadecimal, and prefix it with %. Space is byte 0x20, so it becomes %20. & is 0x26 โ %26. ? โ %3F. # โ %23. / โ %2F. = โ %3D. Now the byte is data, not structure.
For anything outside ASCII โ รฉ, emoji, Chinese characters โ the character is first turned into its UTF-8 bytes, then each byte is encoded. รฉ is two bytes in UTF-8 (0xC3 0xA9), so it encodes to %C3%A9. A single emoji is four bytes, which becomes four percent groups. That's why one accented letter can turn into a six-character blob.
Two buckets: reserved and unreserved
The spec (RFC 3986) sorts characters into two useful groups.
Unreserved characters are always safe: AโZ, aโz, 0โ9, and the four marks - _ . ~. They never need encoding, and you shouldn't encode them. Turning . into %2E is technically legal but pointless, and some servers handle it poorly.
Reserved characters are the delimiters: : / ? # [ ] @ ! $ & ' ( ) * + , ; =. Encode one only when you want the literal character instead of its structural job.
Everything else โ spaces, most punctuation, accented letters โ has no place in a raw URL and must be encoded.
The judgment call is context. A / between path segments is a separator and stays as-is. A / inside a query value is just a slash you want to keep, so it becomes %2F there.
%20 vs +: the space bug that wastes an afternoon
In the path, a space is always %20. /my file.pdf โ /my%20file.pdf. No exceptions.
In the query string there are two conventions. The application/x-www-form-urlencoded format โ what HTML forms submit โ encodes a space as +. So ?q=hello+world and ?q=hello%20world usually both decode to "hello world."
The trap runs both ways:
- Put a
+in a path expecting a space and it stays a literal plus./hello+worldis the string "hello+world," not "hello world." - Put a literal
+in a query value โ a phone number+1 555..., a search for "C++" โ without encoding it, and the parser reads it as a space. "C++" silently becomes "C ". To carry a real plus sign, encode it as%2B.
A rule that always works: use %20 for spaces everywhere, and encode a literal + as %2B. %20 is valid in both the path and the query, so you never have to reason about which component you're standing in.
When a broken link is just a missing encode
A surprising number of "the link doesn't work" bugs are one un-encoded character. The symptoms are recognizable:
- Everything after a
#disappears. The#starts the fragment, which the server never receives. A value likePa#wordgets truncated toPa. Encode it as%23. - A link with a space breaks when pasted into email, chat, or Markdown โ the client ends the link at the space.
%20fixes it. - A value containing
?or&scrambles later parameters, exactly like the Ben & Jerry case above. - A redirect URL passed as a parameter โ
?next=https://site.com/a?b=2โ falls apart because its own?and&collide with the outer query. The whole inner URL has to be encoded as a single value.
When you hit one of these, paste the string into an encoder, encode just the offending value, and drop it back in. everyboringtool.com's URL Encode/Decode runs entirely in the browser with nothing uploaded, so it's safe to push a URL carrying a token or a customer email through it. Decoding works in reverse too โ paste a %C3%A9-riddled link to read what it actually says.
When encoding is the wrong move (honest limits)
Percent-encoding is per-value, not per-URL, and a few habits break it:
- Don't encode the whole URL. Run
https://example.com/a?b=cthrough an encoder and you gethttps%3A%2F%2F...โ the://, the?, and the&that give the link its shape are now data, and the link is dead. Encode only the piece that's user input: one parameter value, one path segment. - Don't double-encode. Encoding an already-encoded string turns
%20into%2520, because the%itself becomes%25. If you see%25where you expected a space, something ran through the encoder twice. Decode once and check. - Encoding isn't security. It stops characters from breaking your URL's structure; it does not sanitize input or block injection. A value can be perfectly encoded and still be hostile once decoded, so validate on the server regardless.
- It's not for secrets. Encoding is fully reversible by anyone โ a formatting convention, not encryption. Don't drop something sensitive into a URL and assume
%-encoding hides it.
When you build a URL by hand, encode each dynamic value on its own, use %20 for spaces so you never have to think about +, and leave the structural / ? & = # untouched. When a link breaks, look for the one character carrying meaning it wasn't supposed to.