8 min read

URL Encoding vs Base64: When to Use Each

Compare URL percent-encoding and Base64: understand character sets, byte overhead, why standard Base64 breaks URLs, and how to use URL-safe Base64 correctly.

When building web applications and REST APIs, developers constantly transmit data across network boundaries: search parameters, redirect targets, authentication tokens, cryptographic signatures, and binary files. Choosing the wrong encoding format for these payloads causes subtle, frustrating production bugs—such as altered query parameters, corrupted binary blobs, broken routing, or bloated network payloads.

Two formats dominate web transport: URL encoding (percent-encoding) and Base64 encoding. While both convert data into ASCII characters that survive network transmission, they were designed for entirely different protocols, have radically different byte overheads, and behave differently inside URLs.

You can encode, decode, and inspect both formats directly in your browser using ToolPorta's URL Encode & Decode tool and Base64 Encode & Decode tool. This guide analyzes how each format works under official RFC specifications, calculates their exact memory and bandwidth overheads, exposes the notorious "Base64 in URLs" trap, and provides clear criteria for choosing the right tool for your system architecture.

What is URL encoding (percent-encoding)?#

URL encoding—formally standardized as percent-encoding in RFC 3986—is an escaping mechanism specifically designed for Uniform Resource Identifiers (URIs).

URIs rely on a small set of characters to define syntax and boundaries. For example, : separates schemes, // denotes authority, / separates path segments, ? introduces query strings, and & separates query parameters. If dynamic user data contains any of these characters without escaping, HTTP parsers and web servers will misinterpret the URL's structural boundaries.

RFC 3986 character classification#

RFC 3986 categorizes all standard ASCII characters into three distinct groups:

  1. Unreserved characters (A-Z, a-z, 0-9, -, _, ., ~): These have no reserved structural meaning and URI producers normally leave them unencoded.
  2. Reserved characters (: / ? # [ ] @ ! $ & ' ( ) * + , ; =): These characters act as delimiters. When one of these characters represents literal payload data rather than a structural delimiter, it must be escaped.
  3. Disallowed characters: Spaces, control characters (0x00–0x1F), and non-ASCII Unicode characters (such as accents or emojis) are not permitted in raw URIs and must always be encoded.

How percent-encoding works#

Percent-encoding replaces any forbidden or delimiter byte with a three-character triplet: a percent sign (%) followed by two hexadecimal digits representing the raw byte value (0x00 through 0xFF):

  • A space (0x20) becomes %20.
  • An ampersand (&, 0x26) becomes %26.
  • A forward slash (/, 0x2F) becomes %2F.
  • An equals sign (=, 0x3D) becomes %3D.

For multi-byte Unicode characters, the string is first converted to UTF-8 bytes, and each individual byte is percent-encoded:

  • The letter é (UTF-8 bytes 0xC3 0xA9) becomes %C3%A9.
  • The rocket emoji 🚀 (UTF-8 bytes 0xF0 0x9F 0x9A 0x80) becomes %F0%9F%9A%80.

What is Base64 encoding?#

Base64 encoding—standardized in RFC 4648—was created to solve a different engineering challenge: transmitting arbitrary binary data across legacy network systems that were historically designed to handle only 7-bit printable ASCII text (such as SMTP email).

Instead of escaping individual characters as triplets, Base64 treats the entire input stream as raw binary bits. It takes groups of 3 bytes (24 bits) and splits them into 4 6-bit chunks. Each 6-bit chunk maps to one of 64 printable characters from the Base64 alphabet:

  • Uppercase letters: A to Z (indices 0–25)
  • Lowercase letters: a to z (indices 26–51)
  • Digits: 0 to 9 (indices 52–61)
  • Symbols: + (index 62) and / (index 63)
  • Padding: = (appended when the input byte count is not divisible by 3)

Base64 is widely used for email MIME attachments, data URIs (data:image/png;base64,...), sending binary encryption keys inside JSON payloads, and basic authorization headers.

Base64 is purely an encoding scheme for data transport, not an encryption method. Anyone can decode Base64 in milliseconds without a secret key. For a comprehensive security analysis, read our guide on Base64 Is Encoding, Not Encryption.

Comprehensive comparison: URL encoding vs Base64#

DimensionURL Encoding (Percent-Encoding)Standard Base64 EncodingURL-Safe Base64 (base64url)
Primary StandardRFC 3986RFC 4648 (§4)RFC 4648 (§5)
Primary PurposeEscape URI delimiters in stringsRepresent binary data as ASCII textRepresent binary data safely inside URIs
Allowed Alphabet%, 0-9, A-F, plus unreserved charsA-Z, a-z, 0-9, +, /, =A-Z, a-z, 0-9, -, _ (no =)
Overhead on ASCII Text0% for unreserved characters; up to 200% per escaped byteAbout 33% for large inputs, plus padding effectsAbout 33% for large inputs; padding may be omitted by agreement
Overhead on Binary DataData-dependent, with a 200% maximum per encoded byteAbout 33% for large inputsAbout 33% for large inputs
Safe for URL Path SegmentsYes (escapes / as %2F)No (contains raw /)Yes (replaces / with _)
Safe for Query ParametersYes (escapes &, =, +)No (+ decodes as space; = breaks parsing)Yes (uses - and _; omits =)
Padding RequiredNoYes (= or == at end)No (padding stripped by convention)
Handling of SpacesEscaped as %20 (or + in forms)Encoded like any other byteEncoded like any other byte

The "Base64 in URLs" trap: Why standard Base64 breaks query parameters#

A common developer mistake is taking a standard Base64 string and placing it directly into a URL query parameter or path:

TEXT
1https://api.example.com/verify?token=a+b/c=

This frequently fails in production. Standard Base64 uses three characters that violate URI parsing rules:

1. The plus sign (+) turns into a space#

In application/x-www-form-urlencoded data, used by HTML forms and by many query-string parsers, + represents a space. RFC 3986 URI query syntax itself does not assign that meaning to +, so behavior depends on the parser.

When your backend framework (Node.js Express, Python Django/Flask, Java Spring, or Go net/url) parses ?token=a+b/c=, it automatically decodes the + into a literal space:

TEXT
1Sent by client:      "a+b/c="2Received by backend: "a b/c="

When your server attempts to decode "a b/c=" as Base64, the decoder encounters an invalid character ( ) and throws an exception (Invalid character in Base64 string or binascii.Error: Incorrect padding).

2. The forward slash (/) breaks URL path routing#

If you pass a standard Base64 string inside a URL path segment (such as /api/v1/files/a+b/c=), web servers like Nginx or Apache treat / as a directory separator. The server routes the request to a non-existent route /api/v1/files/a+b/ with child resource c=, returning an immediate 404 Not Found.

Even if percent-encoded as %2F, many production reverse proxies reject encoded slashes by default for security reasons (such as Apache's AllowEncodedSlashes Off directive to prevent directory traversal attacks).

3. The equals sign (=) confuses query string parsers#

Base64 uses = for padding at the end of the string. In query strings, = separates parameter names from parameter values (key=value). While modern query parsers usually handle = in values correctly, naive splitters and regular expressions can break when encountering trailing padding characters.

The solution: URL-Safe Base64 (base64url)#

To resolve this incompatibility, RFC 4648 Section 5 defined URL-Safe Base64 (commonly referred to as base64url).

URL-safe Base64 makes two character substitutions. Padding may be omitted when the surrounding specification makes the data length implicit:

  1. Replace + with hyphen (-)
  2. Replace / with underscore (_)
  3. Omit trailing = only when the protocol permits it
TEXT
1Standard Base64:   "u/3+4A=="2URL-Safe Base64:   "u_3-4A"

An unpadded base64url value contains only URI-unreserved characters, so it can usually be placed in a path segment, query value, or fragment without percent-encoding. The surrounding URL grammar and application protocol still determine whether padding or additional delimiters are allowed.

JSON Web Tokens use unpadded base64url segments separated by periods (header.payload.signature). Whether a JWT should appear in a URL is a separate security decision because URLs may be stored in history, logs, referrer data, and analytics systems.

Decision framework: When to use which format#

Follow this decision tree when selecting an encoding format for your application:

TEXT
1What kind of data are you transmitting?2 │3 ├── Plain text, search terms, or parameter values inside a URL?4 │    └──> Use URL Encoding (percent-encoding via encodeURIComponent)5 │6 ├── Binary data (images, files, crypto keys) inside JSON or email?7 │    └──> Use Standard Base64 (RFC  §)8 │9 └── Binary data, hashes, or tokens that must be placed inside a URL?10      └──> Use URL-Safe Base64 (base64url / RFC  §)

Practical scenarios:#

  • Search query parameter (?q=c++ & python): Use URL encoding. It escapes + as %2B, spaces as %20, and & as %26, producing ?q=c%2B%2B%20%26%20python.
  • Redirect URL in a query parameter (?next=/dashboard?tab=billing): Use URL encoding. The entire target URL must be escaped using encodeURIComponent so its internal ? and = do not corrupt the outer query string.
  • Embedded image in CSS or HTML (data:image/png;base64,...): Use Standard Base64. Data URIs expect standard Base64 representation.
  • A new opaque token carrying random bytes: Base64url is a practical text representation. Treat OAuth authorization codes and other externally supplied tokens as opaque values and do not re-encode them unless their protocol explicitly requires it.
  • Cryptographic signature in an API query parameter: Use URL-Safe Base64 (base64url). If you must use standard Base64, you must pass it through URL encoding as well (e.g., encodeURIComponent(base64String)), but base64url is cleaner and shorter.

Implementation recipes in JavaScript and Python#

JavaScript / TypeScript#

Modern JavaScript provides native utilities for URL encoding and URL-safe Base64 transformations.

TypeScript
1// 1. URL Encoding a query parameter value2const queryParam = "c++ & rust/wasm";3const urlEncoded = encodeURIComponent(queryParam);4console.log(urlEncoded);5// Output: "c%2B%2B%20%26%20rust%2Fwasm"67// 2. Building a full URL with URLSearchParams (automatically percent-encodes values)8const params = new URLSearchParams({9  search: "c++ & rust",10  redirect: "https://example.com/callback?id=123",11});12console.log(params.toString());13// Output: "search=c%2B%2B+%26+rust&redirect=https%3A%2F%2Fexample.com%2Fcallback%3Fid%3D123"1415// 3. Converting Standard Base64 to URL-Safe Base64 (base64url)16function toBase64Url(base64: string): string {17  return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");18}1920// 4. Converting URL-Safe Base64 back to Standard Base64 for decoding21function fromBase64Url(base64url: string): string {22  let base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");23  while (base64.length %  !== ) {24    base64 += "=";25  }26  return base64;27}2829const standard = "a+b/c==";30const safe = toBase64Url(standard);31console.log(safe); // "a-b_c"32console.log(fromBase64Url(safe)); // "a+b/c=="

Python#

Python's standard library provides dedicated modules for both URI manipulation and URL-safe Base64:

Python
1import urllib.parse2import base6434# 1. URL Encoding query parameter values5raw_param = "c++ & rust/wasm"6# quote() uses %20 for spaces; quote_plus() uses + for spaces7encoded_rfc3986 = urllib.parse.quote(raw_param, safe="")8print(encoded_rfc3986)9# Output: "c%2B%2B%20%26%20rust%2Fwasm"1011# 2. URL-Safe Base64 Encoding12raw_bytes = b"\xfb\xef\xff\x00"13safe_b64 = base64.urlsafe_b64encode(raw_bytes).decode("ascii").rstrip("=")14print(safe_b64)15# Output uses '-' and '_' instead of '+' and '/'1617# 3. URL-Safe Base64 Decoding (restoring padding automatically)18def decode_base64url(safe_str: str) -> bytes:19    padding =  - (len(safe_str) % )20    if padding != :21        safe_str += "=" * padding22    return base64.urlsafe_b64decode(safe_str)2324decoded = decode_base64url(safe_b64)25assert decoded == raw_bytes

Frequently asked questions#

Can I just URL-encode a standard Base64 string instead of using base64url?#

Yes. Percent-encoding +, /, and = preserves a standard Base64 value in a query parameter when both sides follow the same parsing rules. Base64url is often cleaner for protocols you design, but do not transform an opaque token defined by another protocol.

What is the difference between encodeURI and encodeURIComponent in JavaScript?#

encodeURI is intended for complete URLs; it preserves protocol and structural delimiters (:, /, ?, #, &, =) and only encodes invalid characters like spaces or Unicode symbols. encodeURIComponent is intended for individual parameter keys or values; it aggressively escapes all reserved delimiters (including /, ?, &, and =). Never use encodeURI on query parameter values, or characters like & and = will remain unescaped and break your query structure.

Should spaces be encoded as %20 or +?#

Use %20 for standard RFC 3986 compliance, URL path segments, and modern REST APIs. The plus sign (+) is valid for spaces only in HTML form submissions using application/x-www-form-urlencoded query strings. In URL path segments, a + represents a literal plus character, not a space. Using %20 is universally safe across all URI components.

Validate, transform, and debug your encoded payloads instantly in your browser:

  • Use the URL Encode & Decode tool to convert full URLs or individual components with custom space and hex casing options.
  • Use the Base64 Encode & Decode tool to switch between standard and URL-safe Base64 formats for text and files.
  • Use the Hash Generator to generate SHA-256 and MD5 digests and compare their Base64 representations.
  • Read our JavaScript URL encoding guide and equivalent Python examples for backend integration.