DevUtils
Guides
Web & Protocols5 min read

URL Percent-Encoding (RFC 3986) & encodeURI vs encodeURIComponent

A comprehensive technical guide to URI percent-encoding standards under RFC 3986, resolving query string "+" vs "%20" ambiguities, UTF-8 multi-byte octets, and JavaScript encoding functions.

Interactive Developer Tool

URL Encoder / Decoder

Validate, test, and debug this directly in your browser without sending any data to a server.

Launch Tool

Why URL Percent-Encoding Exists in the RFC 3986 Standard

A Uniform Resource Identifier (URI), as defined by IETF RFC 3986, is composed of a strictly limited set of US-ASCII characters. Because URIs must be transmitted reliably across heterogeneous network infrastructure (including legacy routers, email protocols, and command-line shells), characters outside the printable ASCII range—as well as characters reserved for structural URI syntax—cannot appear as raw literal bytes in a URL.

To represent arbitrary characters, binary bytes, or non-ASCII unicode scripts (such as Korean, Japanese, or emojis), the specification defines Percent-Encoding (also called URL encoding). In percent-encoding, any unpermitted character is first converted into its UTF-8 octet sequence, and each resulting byte is represented by a triplet consisting of the percent symbol (%) followed by two hexadecimal digits.

The "+" vs. "%20" Confusion: Query Strings vs. URI Paths

One of the most persistent bugs in web development is the inconsistent handling of whitespace characters across URLs:

  • RFC 3986 Standard (%20): In generic URI paths (e.g., /api/documents/my%20file.pdf), a space character must always be encoded as %20. The plus sign (+) is a reserved delimiter character and is interpreted literally as a plus sign.
  • HTML Form Standard (+): In the legacy W3C application/x-www-form-urlencoded media type (historically used by HTML <form> submissions), spaces within query strings were mapped to the plus symbol (?search=hello+world).

When modern web servers receive a query parameter containing +, some decoders interpret it as a literal plus sign while others treat it as a space, causing corrupted search tokens or broken signature verifications. Pick one convention per API and use it on both ends: URLSearchParams follows the form encoding and emits + for spaces (see the example below), while encodeURIComponent always emits %20. Encoding with one and decoding with the other is where these bugs come from.

JavaScript Encoding Functions: encodeURI vs encodeURIComponent

JavaScript provides two native global encoding primitives with critical architectural distinctions:

  • encodeURI(uri): Designed to encode a complete, full URI. It deliberately preserves all characters that serve as structural protocol delimiters: ; / ? : @ & = + $ , #. If you pass a query parameter value that contains an ampersand (&) or question mark (?), encodeURI will not escape it, corrupting the query parameter structure.
  • encodeURIComponent(component): Designed to encode an isolated query parameter key or value. It aggressively encodes all reserved syntactic characters except A-Z a-z 0-9 - _ . ! ~ * ' ( ). Always use encodeURIComponent when assembling query string pairs.
JAVASCRIPTDevUtils Code Reference
const param = "user&role=admin";

// ❌ DANGEROUS: encodeURI does not encode '&' or '='
const badUrl = "https://api.devutils.run/search?query=" + encodeURI(param);
// Result: https://api.devutils.run/search?query=user&role=admin (Interpreted as two separate params!)

// ✅ CORRECT: encodeURIComponent properly escapes reserved delimiters
const safeUrl = "https://api.devutils.run/search?query=" + encodeURIComponent(param);
// Result: https://api.devutils.run/search?query=user%26role%3Dadmin (Safely preserved)

UTF-8 Multi-Byte Character Encoding Mechanics

When encoding non-ASCII international scripts, percent-encoding operates on UTF-8 byte octets rather than UTF-16 code units:

  • The English letter 'A' is ASCII code 65 (0x41), requiring no encoding.
  • The Korean character '가' is Unicode U+AC00. In UTF-8, it encodes to 3 bytes: 0xEA 0xB0 0x80. The resulting percent-encoded string is %EA%B0%80.
  • The Japanese hiragana 'あ' is Unicode U+3042. In UTF-8, it encodes to 3 bytes: 0xE3 0x81 0x82, yielding %E3%81%82.
  • The emoji '🚀' (Rocket) is Unicode U+1F680. In UTF-8, it requires 4 bytes: 0xF0 0x9F 0x9A 0x80, producing %F0%9F%9A%80.

Legacy encoders that operated on Latin-1 (ISO-8859-1) would produce garbled mojibake tokens. Modern web standards mandate UTF-8 octet serialization.

Production Standards: Using the WHATWG URL API

Manual string concatenation of URLs is brittle and prone to injection bugs. Modern browser runtimes and Node.js provide the standardized WHATWG URL and URLSearchParams classes, which handle character escaping and boundary management automatically.

JAVASCRIPTDevUtils Code Reference
// Modern, robust URL construction with WHATWG API
const endpoint = new URL("https://api.devutils.run/v1/search");
endpoint.searchParams.set("query", "data science & AI");
endpoint.searchParams.set("tags", "python/django");
endpoint.searchParams.set("user", "홍길동");

console.log(endpoint.toString());
// Output: https://api.devutils.run/v1/search?query=data+science+%26+AI&tags=python%2Fdjango&user=%ED%99%8D%EA%B8%B8%EB%8F%99
Tags:URL EncodeWeb StandardsRFC 3986HTTPJavaScript
Interactive Developer Tool

URL Encoder / Decoder

Encode and decode URLs online for free. Supports encodeURIComponent (for query string values) and encodeURI (for complete URLs). Convert special characters to percent-encoded format instantly — no signup.

Launch Tool

Recommended Guides

View all guides →