URL Encoding Explained

URLs can only safely carry a limited set of characters, so everything else gets converted into <code>%HH</code> escape codes. Here's how percent-encoding works and how to apply it correctly.

What is URL encoding?

URL encoding — formally called percent-encoding — is the mechanism that lets a URL carry characters that would otherwise be ambiguous or illegal. A URL is, at its core, just text built from a small, well-defined character set. When you need to include a character that isn't in that set, or a character that already has a special structural meaning, you replace it with a percent sign (%) followed by two hexadecimal digits representing that character's byte value in UTF-8.

For example, a space becomes %20, an ampersand becomes %26, and a forward slash becomes %2F. The two hex digits after the % spell out the byte: 0x20 is 32, the ASCII code for a space. Characters outside ASCII are first encoded to UTF-8, then each byte is percent-encoded — so an em dash (—) becomes %E2%80%94, three bytes, three escapes.

Reserved vs. unreserved characters

The URL specification (RFC 3986) splits characters into groups. The distinction is what drives every encoding decision.

Unreserved characters never need encoding. They are safe to appear literally anywhere:

  • Letters A-Z and a-z
  • Digits 0-9
  • The four symbols - _ . ~

Reserved characters have a structural job inside a URL — they act as delimiters that separate one part from another:

: / ? # [ ] @ ! $ & ' ( ) * + , ; =

A colon separates the scheme from the rest; a ? starts the query string; & separates query parameters; # starts a fragment, and so on. When one of these characters is meant as a delimiter, leave it alone. When it is meant as literal data inside a value, you must encode it — otherwise the parser will mistake your data for structure.

encodeURIComponent vs. encodeURI

This is the single most important distinction in practice, and choosing the wrong one is the most common URL bug. JavaScript ships two functions, and they differ entirely in scope.

encodeURIComponent — Component scope

Use this for a single piece of a URL: one query parameter value, or one path segment. It assumes the text is data, not structure, so it encodes the reserved delimiters too — including &, =, ?, /, and #. That is exactly what you want when a value might itself contain those characters.

const value = "a&b=c?d";
encodeURIComponent(value);
// "a%26b%3Dc%3Fd"

const url = "https://example.com/search?q=" + encodeURIComponent(value);
// the &, =, ? inside the value can't break the query string

encodeURI — Full URL scope

Use this when you have an entire, already-structured URL and only want to escape illegal characters (like spaces) without destroying the URL's anatomy. It deliberately leaves the reserved delimiters intact so that ://, ?, &, and # keep working.

const url = "https://example.com/my docs?tag=a&b";
encodeURI(url);
// "https://example.com/my%20docs?tag=a&b"
// the space is escaped; the ? & / : stay as delimiters

The rule of thumb: encoding a whole URL? use Full scope. Encoding one value to drop into a URL? use Component scope.

Common pitfalls

  • Encoding the whole URL when you meant a component. If you run encodeURI on a string that is really just a single value, embedded & and = characters survive and silently corrupt your query string. Use Component scope for values.
  • Double-encoding. Encoding text that is already encoded turns %20 into %2520, because the % itself becomes %25. The result decodes back to %20 as literal text, not a space. Always encode raw input exactly once, and decode exactly once.
  • Spaces: %20 vs. +. In the path and most components, a space is %20. But in application/x-www-form-urlencoded data — classic HTML form submissions and many query strings — a space is encoded as +, and a literal plus sign becomes %2B. Mixing the two conventions causes plus signs to appear as spaces or vice versa. Know which format the receiving system expects.
  • Forgetting non-ASCII is multi-byte. Accented letters and emoji expand into several %HH pairs under UTF-8. That's correct, not a bug.

Worked examples

Input value:   hello world & co
Component:     hello%20world%20%26%20co
Full (encodeURI of that fragment): hello%20world%20&%20co

Input value:   price=$5/kg
Component:     price%3D%245%2Fkg

Input value:   café
Component:     caf%C3%A9   (é is two UTF-8 bytes)

Notice how Component scope escapes &, =, and / — protecting the value — while Full scope leaves them as delimiters.

Encode and decode with Formatly

Formatly's URL Encode / Decode tool lets you switch between Component and Full scope so you can match the exact behavior of encodeURIComponent or encodeURI, then decode back to verify your work. Paste your text, pick the scope, and read the result instantly. Everything runs 100% in your browser — nothing is uploaded to a server — so even URLs containing API keys, tokens, or personal data never leave your machine.

FAQ

When should I use encodeURIComponent instead of encodeURI?

Use encodeURIComponent (Component scope) when you are encoding a single value — one query parameter or one path segment — because it escapes reserved delimiters like &, =, ?, and / so they can't break the surrounding URL. Use encodeURI (Full scope) only when you have an entire URL and just want to escape illegal characters like spaces while keeping the structure intact.

Why does a space sometimes become %20 and sometimes +?

In a URL path and most components, a space is encoded as %20. In application/x-www-form-urlencoded data — used by classic HTML form submissions and many query strings — a space is encoded as + instead, and a literal plus becomes %2B. Both are valid; you must use whichever convention the receiving system expects, or spaces and plus signs will get swapped.

What is double-encoding and how do I avoid it?

Double-encoding happens when you encode text that was already encoded. The percent sign in %20 itself gets escaped to %25, producing %2520, which decodes back to the literal text %20 rather than a space. Avoid it by encoding raw input exactly once and decoding exactly once. If you see %25 appearing where you didn't expect it, you've likely encoded twice.

Which characters never need URL encoding?

The unreserved characters are always safe to use literally: the letters A-Z and a-z, the digits 0-9, and the four symbols - _ . ~. Everything else is either a reserved delimiter or an unsafe character and may need percent-encoding depending on where it appears in the URL.

Is it safe to encode URLs that contain API keys or tokens?

Yes, with Formatly it's safe. The URL Encode / Decode tool runs entirely in your browser using client-side JavaScript — your input is never uploaded or sent to any server. That means URLs containing API keys, access tokens, session IDs, or personal data stay on your own machine, so you can encode and decode sensitive links without exposure.