Skip to content

URL Encode

Percent-encode a string for use in a URL.

URL encoding (also called percent-encoding) converts bytes that have special meaning in a URL into a %XX form where XX is the byte’s hex value. The tool above encodes input in your browser using encodeURIComponent; nothing leaves your machine. To reverse the process, see the URL decoder.

Why encoding exists

URLs have structure. A character like ? separates the path from the query string. & separates query parameters. # introduces the fragment. / is a path separator. If the value you want to put inside a URL contains those characters, the parser can’t tell where the value ends and the structure begins:

/search?q=cats & dogs
                ^---- is "& dogs" a value or a new parameter?

Encoding makes the byte unambiguous. %26 is always “the literal byte 0x26” (& in ASCII), never the parameter separator. So:

/search?q=cats%20%26%20dogs
                       ^---- definitely a literal &, not a separator

The reserved set

RFC 3986 divides characters into three groups:

GroupCharactersEncoding rule
UnreservedA-Z a-z 0-9 - _ . ~Never need encoding
Reserved! * ' ( ) ; : @ & = + $ , / ? # [ ]Sometimes need encoding (depends on URL component)
Othereverything elseAlways need encoding

The unreserved set is fully URL-safe in any component. Encoding them is allowed but unnecessary — a and %61 are equivalent, but a is human-readable.

Component-specific rules

The reserved set is more nuanced than “always encode”: some characters are reserved only in some parts of the URL.

  • In a path segment, / is a separator. To put a literal / inside a path segment value, encode it as %2F.
  • In a query string, & and = are separators. To put either inside a value, encode them as %26 and %3D.
  • # is always the fragment separator and must be encoded everywhere except as that separator.
  • + in a query string historically means “space” (legacy of HTML form encoding). Some parsers still treat it that way; safer to encode literal + as %2B.
  • Spaces are always encoded — as %20 in paths and most contexts, sometimes as + in query strings (HTML-form-encoded).

JavaScript provides two encoders that draw the line differently:

FunctionEncodesDoesn’t encode
encodeURIspaces, non-ASCII, a few control characters: / ? # [ ] @ ! $ & ' ( ) * + , ; = (the URL syntax characters)
encodeURIComponenteverything except unreservedonly the unreserved set

encodeURI is for encoding a whole URL where the structure characters should remain meaningful. encodeURIComponent is for encoding a value that will be inserted into a component. Almost all real-world use of URL encoding is the latter.

UTF-8 first, then percent

Characters outside ASCII are encoded by first converting the character to UTF-8, then percent-encoding each resulting byte. The Unicode character é (U+00E9) is two bytes in UTF-8 (0xC3 0xA9), which percent-encodes to %C3%A9. The em-dash — (U+2014) is three bytes in UTF-8 (0xE2 0x80 0x94), which encodes to %E2%80%94.

This is why you sometimes see %C3%A9 in URLs and not %E9 — the latter would be a Latin-1 encoding, which most modern web stacks don’t use. URL encoding without UTF-8 conversion produces a sequence that decodes to mojibake on the receiving end.

Common pitfalls

  • Double encoding. Calling encodeURIComponent on a string that’s already percent-encoded turns %20 into %2520 (% itself becomes %25, then the original 20). The receiver decodes once and gets %20 instead of a space. If a value passes through multiple URL-aware layers, each layer should encode/decode exactly once.
  • Encoding the ? separator. https://example.com/search%3Fq=cats is a URL with a path that contains a literal ?, not a search URL. The separator must remain unencoded.
  • Mixing space conventions. + for space is legal in query strings under HTML form encoding but never in path segments. Decoders should know which convention they’re in.
  • Assuming safe characters are safe everywhere. ~ is unreserved per RFC 3986 but some older systems still encoded it. The cure is worse than the disease — modern parsers handle ~ literally — so emit unreserved characters as themselves.

For decoding URL-encoded strings, see the URL decoder. For dissecting a full URL into its components, see the URL parser.