DevToolBox

Base64デコードで日本語が文字化けする原因と対処

btoa("こんにちは")InvalidCharacterErrorを出したり、 一見成功したのに復号すると "ã" だらけになる——UTF-8 を絡めた Base64 で頻出の罠です。 原因は btoa/atobLatin-1 限定であること。 本記事では UTF-8 安全なコードと、Base64URL の扱いをまとめます。

btoa/atob only accept Latin-1 code points, so UTF-8 multi-byte strings get mangled. Use TextEncoder/TextDecoder for a safe round-trip.

TL;DR

1. UTF-8 安全な JavaScript 実装 / UTF-8 safe JavaScript

// Encode (string → Base64)
function utf8ToBase64(s) {
  const bytes = new TextEncoder().encode(s);
  let bin = "";
  bytes.forEach(b => (bin += String.fromCharCode(b)));
  return btoa(bin);
}

// Decode (Base64 → string)
function base64ToUtf8(b64) {
  const bin = atob(b64);
  const bytes = Uint8Array.from(bin, c => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

utf8ToBase64("こんにちは"); // "44GT44KT44Gr44Gh44Gv"
base64ToUtf8("44GT44KT44Gr44Gh44Gv"); // "こんにちは"

2. Base64URL 対応 / Base64URL

JWT や OAuth 関連の Base64URL は URL安全文字セットを使い、末尾パディングを省略します。

function base64UrlDecode(s) {
  const b64 = s.replace(/-/g, "+").replace(/_/g, "/");
  const pad = b64.length % 4;
  const padded = pad ? b64 + "=".repeat(4 - pad) : b64;
  return base64ToUtf8(padded);
}

3. 言語別の正しい書き方 / Other languages

Python

import base64
b64 = base64.b64encode("こんにちは".encode("utf-8")).decode("ascii")
text = base64.b64decode(b64).decode("utf-8")

Shell

echo -n "こんにちは" | base64   # macOS の base64 は -w 0 不要
echo "44GT44KT44Gr44Gh44Gv" | base64 -d

4. よくあるハマり / Common pitfalls

5. English summary

Browser btoa/atob only handle Latin-1 code points, so passing Japanese (UTF-8) strings directly produces garbage or throws InvalidCharacterError. The safe path is: TextEncoder to get UTF-8 bytes, map bytes to a Latin-1 string viaString.fromCharCode, then btoa. Reverse the process for decoding. JWT segments use Base64URL: replace -_ with +/ and pad with =before decoding. Strip whitespace from MIME-wrapped Base64 first.

関連ツール / Related tools

関連ガイド / Related guides