Skip to content
encode2026-07-253 min read

Many developers hit the same problem the first time they Base64-encode Chinese: after encoding and decoding, the Chinese turns into garbage. For example, you encode "你好世界" and decode it back, only to get a pile of characters like 㽠好.

This isn't a bug in the Base64 algorithm itself — it's character encoding at work. This article helps you thoroughly understand the root cause of base64 Chinese garbage and gives you the correct way to handle it.

Why Base64 Encoding Chinese Produces Garbage

Base64 is fundamentally a binary-to-text encoding scheme — it operates on bytes, not characters. The problem is in the "characters → bytes" step:

  • In UTF-8, one Chinese character takes 3 bytes
  • In GBK, one Chinese character takes 2 bytes

If you encode using UTF-8 to turn Chinese into a byte stream, then decode using GBK to restore it, the byte counts don't match — and you get garbage.

A typical mistake:

// Wrong: directly using btoa to encode Chinese
btoa("你好世界");
// Uncaught DOMException: The string to be encoded contains characters outside of the Latin1 range.

The browser's btoa() only supports Latin1 characters (U+0000 to U+00FF) — it throws on Chinese. Some people work around it with escape():

// It runs, but has hidden risks
btoa(escape("你好世界"));
// When decoding, you must pair it with unescape(), or you get garbage
atob(btoa(escape("你好世界"))); // you get escape-encoded text, not the original
unescape(atob(btoa(escape("你好世界")))); // only this restores it

This works, but escape() is deprecated, and if the other party doesn't know to unescape when you transmit cross-platform, the decoded result is garbage.

The Correct Way to Base64-Encode and Decode Chinese

Core principle: first convert the string to a byte array using UTF-8, then Base64-encode; reverse the process when decoding.

function base64EncodeUnicode(str) {
  const bytes = new TextEncoder().encode(str); // string → UTF-8 bytes
  const binary = Array.from(bytes, b => String.fromCharCode(b)).join("");
  return btoa(binary);
}

function base64DecodeUnicode(base64) {
  const binary = atob(base64);
  const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
  return new TextDecoder().decode(bytes); // UTF-8 bytes → string
}

// Test
const encoded = base64EncodeUnicode("你好世界,Hello!");
console.log(encoded); // "5L2g5aW95LiW55WM77yMSGVsbG8h"

const decoded = base64DecodeUnicode(encoded);
console.log(decoded); // "你好世界,Hello!"

Method 2: In Node.js

// Encode
const encoded = Buffer.from("你好世界", "utf-8").toString("base64");
console.log(encoded); // "5L2g5aW95LiW55WM"

// Decode — note you must specify utf-8
const decoded = Buffer.from(encoded, "base64").toString("utf-8");
console.log(decoded); // "你好世界"

// The root of garbage: encoding and decoding used different character sets
const wrongDecode = Buffer.from(encoded, "base64").toString("latin1");
console.log(wrongDecode); // "ä½ å¥½ä¸ç" ← this is the garbage you see

Method 3: Handling Existing GBK-Encoded Data

If your data source is GBK-encoded (common in legacy systems, Windows Chinese environments), you need to convert it to UTF-8 first:

// On the browser side, you can specify the encoding with TextDecoder
const gbkBytes = new Uint8Array([0xC4, 0xE3, 0xBA, 0xC3]); // GBK encoding of "你好"
const text = new TextDecoder("gbk").decode(gbkBytes);
console.log(text); // "你好"

// Then use the standard method for Base64
const base64 = base64EncodeUnicode(text);

Practical Tips to Avoid Garbage

  1. Use UTF-8 end-to-end: From the database to the backend API to the frontend, use UTF-8 everywhere — this eliminates encoding mismatches at the root.

  2. Label the encoding in transit: If you must handle multi-encoding data, explicitly mark the character set in the protocol or file header (e.g. Content-Type: text/plain; charset=utf-8).

  3. Don't mix encoding functions: btoa/atob only handles Latin1 — for Chinese, always go through UTF-8 byte conversion.

  4. Local processing is safer: Base64 encoding/decoding isn't encryption, but if the content contains sensitive information (user data, internal API parameters), prefer a local tool to avoid the data passing through a third-party server.

If you just need to quickly encode or decode a piece of Chinese text, use our Base64 Encoder/Decoder — all computation happens locally in your browser, data is never uploaded to any server, conversion is instant, UTF-8 Chinese is supported, and you won't run into garbage issues.


ad