Base64: Padding Rules and URL-safe Encoding
Base64 encodes binary data into 64 printable ASCII characters. Why do = padding characters appear at the end? Why must URL and JWT tokens use - and _ variants? This article breaks down Base64 mathematics and URL-Safe variants.
1. Problem: 24-bit grouping and the origin of = padding
Base64 splits every 3 bytes (24 bits) into four 6-bit units, mapping each unit (0–63) to an ASCII character. When total bytes are not divisible by 3, remainder bytes require padding: 1 remainder byte (8 bits) receives four zero bits to form two 6-bit units plus two = chars; 2 remainder bytes (16 bits) receive two zero bits to form three 6-bit units plus one = char.
2. Minimal reproduction: standard Base64 vs URL-Safe variants
Standard Base64 containing + and / degrades when transmitted in URLs. The code snippet below demonstrates converting between both formats:
/* Standard Base64: contains + and / plus = padding */
const stdBase64 = "a+b/c==";
/* Misuse: appending standard Base64 directly to a URL query */
// https://example.com/api?data=a+b/c== (+ becomes space, / becomes path delimiter!)
/* URL-Safe Base64 (RFC 4648): + becomes -, / becomes _, = is stripped */
const urlSafeBase64 = "a-b_c";3. Root cause: character conflicts with HTTP/URL syntax
In the standard Base64 alphabet (A-Z, a-z, 0-9, +, /), + and / serve as reserved URI delimiters. RFC 4648 introduced Base64URL to resolve ambiguity: replacing + with -, / with _, and permitting padding = removal in JWTs or URL queries.
4. Recommendation: standard Base64 and Base64URL safe conversion functions
1. To URL-Safe: replace + with -, / with _, and strip trailing = padding optionally. 2. Restore to standard Base64: replace - with +, _ with /, and compute missing = padding using (4 - (len % 4)) % 4. 3. Use unpadded Base64URL to reduce JWT payload sizes.
5. Complete code: URL-Safe Base64 conversion and unpadded restoration helpers
This TypeScript snippet demonstrates bi-directional, lossless conversion between standard Base64 and RFC 4648 URL-Safe variants.
function toUrlSafeBase64(standardBase64: string, stripPadding = true): string {
let urlSafe = standardBase64.replace(/\+/g, "-").replace(/\//g, "_");
if (stripPadding) {
urlSafe = urlSafe.replace(/=+$/, "");
}
return urlSafe;
}
function fromUrlSafeBase64(urlSafeBase64: string): string {
let base64 = urlSafeBase64.replace(/-/g, "+").replace(/_/g, "/");
const padLength = (4 - (base64.length % 4)) % 4;
for (let i = 0; i < padLength; i++) {
base64 += "=";
}
return base64;
}
const stdBase64 = "a+b/c==";
const urlSafe = toUrlSafeBase64(stdBase64, true);
console.log(urlSafe); // "a-b_c"
console.log(fromUrlSafeBase64(urlSafe)); // "a+b/c=="6. Incorrect approaches
Passing raw JWT unpadded payloads directly to atob without restoring -, _, or = padding, causing decoding crashes. Applying URL-Safe replacements mistakenly to image Data URLs.
7. Boundaries: non-mod-4 lengths and strict decoders
Most native decoders require string lengths to be multiples of 4. Unpadded Base64URL strings must append 1 or 2 missing = padding characters before decoding.
8. Verification
Assert URL-safe round trips after canonicalizing input. Cover every length modulo three, padded and unpadded forms, + / - _, empty input, and invalid characters, and cross-check against a standard library.
9. FAQ
Why does Base64 have at most two = padding characters? Remainder bytes modulo 3 can only equal 1 (needs ==) or 2 (needs =); remainder 0 divides evenly with no padding.
Why are JWT Base64 strings unpadded? RFC 7515 specifies removing trailing = padding to minimize Header and Payload sizes in URLs and Cookies.
How much does Base64 increase text size? Exactly 33.3% (3 binary bytes become 4 ASCII characters).
10. Summary
Master Base64 3-to-4 6-bit partitioning math and = padding rules. Use RFC 4648 Base64URL across URLs, Cookies, and JWTs to avoid transmission corruption.
Sources and further reading
Specifications and references for checking the article’s technical details.
- RFC 4648 — Base-N Encodings
RFC Editor
Related articles
Fix btoa Errors: UTF-8, Chinese Text and Emoji
Analyze window.btoa Latin-1 (ASCII 0-255) limits, providing TextEncoder-based UTF-8 Unicode Base64 encoding while clarifying Base64 is not encryption.
Best practicesBase64 Images: Inlining and Performance Trade-offs
Analyze pros/cons of Data URL Base64 inline icons (33% size inflation, CSS parse blocking, missing CDN cache), giving Web Performance rules.
How it worksCode Diff Algorithms: LCS, Myers and Patience
Examine the evolution of sequence comparison algorithms from dynamic programming LCS to Myers Diff and Patience Diff in version control systems.
Keep exploring
Use the related browser tool to test the workflow with your own sample data.
Open related tool