JavaScript Base64 デコード完全ガイド — atob() と Buffer
無料の Base64デコーダー をブラウザで直接使用 — インストール不要。
Base64デコーダー をオンラインで試す →本番環境の認証問題をデバッグするとき、私が最初に手を伸ばすのは Base64 デコーダーです — JWT ペイロード、Webhook 署名、エンコードされた設定値はすべて Base64 文字列の中に隠れています。 JavaScript には Base64 デコードの主要な組み込みアプローチが 2 つあります: atob()(ブラウザ + Node.js 16+)と Buffer.from(encoded, 'base64').toString() (Node.js)— そして元データに Unicode 文字が含まれている場合、両者の動作は大きく異なります。 コードを書かずにすぐにデコードしたい場合は、 ToolDeck's Base64 Decoder がブラウザで即座に処理します。本ガイドでは両環境を対象に — Node.js 16+ と最新ブラウザ(Chrome 80+、Firefox 75+、Safari 14+)— 本番環境で使えるサンプルを紹介します:UTF-8 復元、URL セーフバリアント、JWT デコード、ファイル、 API レスポンス、Node.js ストリーム、そして実際のコードベースで文字化けを引き起こす 4 つのよくある間違いです。
- ✓atob(encoded) はブラウザネイティブで Node.js 16+ ではグローバルに使用できますが、バイナリ文字列を返します — ASCII を超えるコンテンツには TextDecoder を使って UTF-8 テキストを復元してください。
- ✓Buffer.from(encoded, "base64").toString("utf8") は Node.js の慣用的なアプローチで、追加手順なしに UTF-8 を自動的に処理します。
- ✓URL セーフ Base64(JWT で使用)は + を - に、/ を _ に置き換え、= パディングを除去します。atob() を呼び出す前にこれらを復元するか、Node.js 18+ では Buffer.from(encoded, "base64url").toString() を使用してください。
- ✓デコード前に空白と改行を除去してください — GitHub Contents API や多くの MIME エンコーダーは Base64 出力を 1 行 60〜76 文字で折り返します。
- ✓Uint8Array.prototype.fromBase64()(TC39 Stage 3)はすでに Node.js 22+ と Chrome 130+ で利用可能で、将来的に両環境を統一します。
Base64 デコードとは?
Base64 デコードはエンコードの逆操作です — 64 文字の ASCII 表現を元のバイナリデータやテキストに変換します。 4 つの Base64 文字はちょうど 3 バイトにマッピングされます。 エンコードされた文字列の末尾にある = パディング文字は、最後の 3 バイトグループを完成させるために何バイト追加されたかをデコーダーに伝えます。
Base64 は暗号化ではありません — エンコードされた文字列を持つ誰でも完全に逆操作できます。 その目的はトランスポートの安全性です:7 ビット ASCII テキスト用に設計されたプロトコルやストレージ形式は 任意のバイナリバイトを処理できず、Base64 がそのギャップを埋めます。 JavaScript でよくあるデコードシナリオには、JWT ペイロードの検査、環境変数からの Base64 エンコードされた JSON 設定の展開、REST API からのバイナリファイルコンテンツの抽出、 ブラウザでの data URI のデコードなどがあります。
ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
deploy-bot:sk-prod-a7f2c91e4b3d8
atob() — ブラウザネイティブのデコード関数
atob()(ASCII-to-binary)は IE10 からブラウザで使用可能で、 WinterCG 互換性イニシアチブの一環として Node.js 16.0 でグローバル関数になりました。 Deno、Bun、Cloudflare Workers でもネイティブに動作し、インポートは不要です。
この関数はバイナリ文字列を返します:各文字のコードポイントが 1 つの生のバイト値(0–255)に等しい JavaScript 文字列です。これは重要です:元のデータが U+007F 以上の文字(アクセント付き文字、キリル文字、CJK、絵文字)を 含む UTF-8 テキストだった場合、返される文字列は読めるテキストではなく生のバイト列です。 復元するには TextDecoder を使います(次のセクションで説明)。
最小限の動作例
// Decoding an HTTP Basic Auth credential pair received in a request header
// Authorization: Basic ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
function parseBasicAuth(header: string): { serviceId: string; apiKey: string } {
const base64Part = header.replace(/^Basics+/i, '')
const decoded = atob(base64Part)
const [serviceId, apiKey] = decoded.split(':')
return { serviceId, apiKey }
}
const auth = parseBasicAuth('Basic ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=')
console.log(auth.serviceId) // deploy-bot
console.log(auth.apiKey) // sk-prod-a7f2c91e4b3d8ラウンドトリップ検証
// Verify lossless recovery for ASCII-only content const original = 'service:payments region:eu-west-1 env:production' const encoded = btoa(original) const decoded = atob(encoded) console.log(encoded) // c2VydmljZTpwYXltZW50cyByZWdpb246ZXUtd2VzdC0xIGVudjpwcm9kdWN0aW9u console.log(decoded === original) // true
atob() と btoa() は WinterCG Minimum Common API の一部です — 非ブラウザランタイムでの Fetch、URL、crypto を管理する同じ仕様です。 Node.js 16+、Bun、Deno、Cloudflare Workers で同一に動作します。デコード後の UTF-8 テキストの復元
atob() の最もよくある落とし穴は、 その戻り値の型を誤解することです。元のテキストが Base64 エンコード前に UTF-8 でエンコードされていた場合、atob() は読めるテキストではなく Latin-1 バイナリ文字列を返します:
// '田中太郎' was UTF-8 encoded then Base64 encoded before transmission const encoded = '55Sw5Lit5aSq6YOO' // ❌ atob() returns the raw UTF-8 bytes as a Latin-1 string — garbled output console.log(atob(encoded)) // "ç"°ä¸å¤ªé ã" ← byte values misread as Latin-1
正しいアプローチは TextDecoder を使って生のバイトを UTF-8 として解釈することです:
TextDecoder アプローチ — あらゆる Unicode 出力に対応
// Unicode-safe Base64 decode utilities
function fromBase64(encoded: string): string {
const binary = atob(encoded)
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0))
return new TextDecoder().decode(bytes)
}
function toBase64(text: string): string {
const bytes = new TextEncoder().encode(text)
const chars = Array.from(bytes, byte => String.fromCharCode(byte))
return btoa(chars.join(''))
}
// Works with any language or script
const orderNote = '確認済み:田中太郎 — 東京倉庫、数量:250'
const encoded = toBase64(orderNote)
const decoded = fromBase64(encoded)
console.log(decoded === orderNote) // true
console.log(decoded)
// 確認済み:田中太郎 — 東京倉庫、数量:250Buffer.from(encoded, 'base64').toString('utf8') を使ってください。 デコードされたバイトを自動的に UTF-8 として解釈し、大きな入力では高速です。Node.js の Buffer.from() — 完全デコードガイド
Node.js では、Buffer が Base64 デコードを含む すべてのバイナリ操作の慣用的な API です。UTF-8 をネイティブに処理し、 真の Buffer(バイナリセーフ)を返し、 Node.js 18 以降は URL セーフバリアントの 'base64url' エンコーディングショートカットをサポートします。
環境変数の設定をデコードする
// Server config stored as Base64 in an env variable (avoids JSON escaping in shell)
// DB_CONFIG=eyJob3N0IjoiZGItcHJpbWFyeS5pbnRlcm5hbCIsInBvcnQiOjU0MzIsImRhdGFiYXNlIjoiYW5hbHl0aWNzX3Byb2QiLCJtYXhDb25uZWN0aW9ucyI6MTAwfQ==
const raw = Buffer.from(process.env.DB_CONFIG!, 'base64').toString('utf8')
const dbConfig = JSON.parse(raw)
console.log(dbConfig.host) // db-primary.internal
console.log(dbConfig.port) // 5432
console.log(dbConfig.maxConnections) // 100.b64 ファイルからバイナリファイルを復元する
import { readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
// Read the Base64-encoded certificate and restore the original binary
const encoded = readFileSync(join(process.cwd(), 'dist', 'cert.b64'), 'utf8').trim()
const certBuf = Buffer.from(encoded, 'base64')
writeFileSync('./ssl/server.crt', certBuf)
console.log(`Restored ${certBuf.length} bytes`)
// Restored 2142 bytesエラーハンドリング付き非同期デコード
import { readFile, writeFile } from 'node:fs/promises'
async function decodeBase64File(
encodedPath: string,
outputPath: string,
): Promise<number> {
try {
const encoded = await readFile(encodedPath, 'utf8')
const binary = Buffer.from(encoded.trim(), 'base64')
await writeFile(outputPath, binary)
return binary.length
} catch (err) {
const code = (err as NodeJS.ErrnoException).code
if (code === 'ENOENT') throw new Error(`File not found: ${encodedPath}`)
if (code === 'EACCES') throw new Error(`Permission denied: ${encodedPath}`)
throw err
}
}
// Restore a PDF stored as Base64
const bytes = await decodeBase64File('./uploads/invoice.b64', './out/invoice.pdf')
console.log(`Decoded ${bytes} bytes — PDF restored`)Base64 デコード関数 — パラメータリファレンス
コードを書いたりレビューしたりする際に参照できる、2 つの主要なネイティブデコード API のパラメータクイックリファレンスです。
atob(encodedData)
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
| encodedData | string | はい | +、/、= 文字を使った標準 Base64 文字列。URL セーフバリアント(-、_)は InvalidCharacterError をスローします。空白は許可されません。 |
Buffer.from(input, inputEncoding) / .toString(outputEncoding)
| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
| input | string | Buffer | TypedArray | ArrayBuffer | 必須 | デコードする Base64 エンコードされた文字列、またはエンコードされたバイトを含む Buffer。 |
| inputEncoding | BufferEncoding | "utf8" | 標準 Base64(RFC 4648 §4)は "base64"、URL セーフ Base64(RFC 4648 §5、Node.js 18+)は "base64url" を指定。 |
| outputEncoding | string | "utf8" | .toString() 出力のエンコーディング。読めるテキストには "utf8"、atob() 出力と互換の Latin-1 バイナリ文字列には "binary" を使用。 |
| start | integer | 0 | デコードされた Buffer 内で読み込みを開始するバイトオフセット。.toString() の第 2 引数として渡される。 |
| end | integer | buf.length | 読み込みを停止するバイトオフセット(排他)。.toString() の第 3 引数として渡される。 |
URL セーフ Base64 — JWT と URL パラメータのデコード
JWT はすべての 3 つのセグメントに URL セーフ Base64(RFC 4648 §5)を使用します。 URL セーフ Base64 は + を - に、 / を _ に置き換え、末尾の = パディングを除去します。 復元せずにこれを atob() に渡すと、 誤った出力が生成されるかスローされます。
ブラウザ — デコード前に文字とパディングを復元する
function decodeBase64Url(input: string): string {
const base64 = input.replace(/-/g, '+').replace(/_/g, '/')
const padded = base64 + '==='.slice(0, (4 - base64.length % 4) % 4)
const binary = atob(padded)
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0))
return new TextDecoder().decode(bytes)
}
// Inspect a JWT payload segment (the middle part between the two dots)
const jwtToken = 'eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsIndvcmtzcGFjZUlkIjoid3NfM2E3ZjkxYzIiLCJleHAiOjE3MTcyMDM2MDB9'
const payload = decodeBase64Url(jwtToken)
const claims = JSON.parse(payload)
console.log(claims.userId) // usr_9f2a1c3e8b4d
console.log(claims.role) // editor
console.log(claims.workspaceId) // ws_3a7f91c2Node.js 18+ — ネイティブ 'base64url' エンコーディング
// Node.js 18 added 'base64url' as a first-class Buffer encoding — no manual replace needed
function decodeJwtSegment(segment: string): Record<string, unknown> {
const json = Buffer.from(segment, 'base64url').toString('utf8')
return JSON.parse(json)
}
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsIndvcmtzcGFjZUlkIjoid3NfM2E3ZjkxYzIiLCJleHAiOjE3MTcyMDM2MDB9.SIGNATURE'
const [headerB64, payloadB64] = token.split('.')
const header = decodeJwtSegment(headerB64)
const payload = decodeJwtSegment(payloadB64)
console.log(header.alg) // HS256
console.log(payload.role) // editor
console.log(payload.workspaceId) // ws_3a7f91c2ファイルと API レスポンスからの Base64 デコード
本番コードでは、Base64 デコードはエンコードされた形式でコンテンツを提供する外部 API を 消費するときに最も頻繁に発生します。両シナリオとも空白の扱いとバイナリ vs テキスト出力に関して 重要な注意点があります。デバッグ中にエンコードされたレスポンスを確認するだけなら、 直接 Base64 Decoder に貼り付けてください — 標準と URL セーフモードを即座に処理します。
GitHub Contents API からのコンテンツをデコードする
// GitHub Contents API returns file content as Base64, wrapped at 60 chars per line
async function fetchDecodedFile(
owner: string,
repo: string,
path: string,
token: string,
): Promise<string> {
const res = await fetch(
`https://api.github.com/repos/${owner}/${repo}/contents/${path}`,
{ headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github.v3+json' } }
)
if (!res.ok) throw new Error(`GitHub API ${res.status}: ${res.statusText}`)
const data = await res.json() as { content: string; encoding: string }
if (data.encoding !== 'base64') throw new Error(`Unexpected encoding: ${data.encoding}`)
// ⚠️ GitHub wraps at 60 chars — strip newlines before decoding
const clean = data.content.replace(/\n/g, '')
return Buffer.from(clean, 'base64').toString('utf8')
}
const openApiSpec = await fetchDecodedFile('acme-corp', 'platform-api', 'openapi.json', process.env.GITHUB_TOKEN!)
const spec = JSON.parse(openApiSpec)
console.log(`API version: ${spec.info.version}`)API からの Base64 エンコードされたバイナリをデコードする(ブラウザ)
// Some APIs return binary content (images, PDFs) as Base64 JSON fields
async function downloadDecodedFile(endpoint: string, authToken: string): Promise<void> {
const res = await fetch(endpoint, { headers: { Authorization: `Bearer ${authToken}` } })
if (!res.ok) throw new Error(`Download failed: ${res.status}`)
const { filename, content, mimeType } = await res.json() as {
filename: string; content: string; mimeType: string
}
// Decode Base64 → binary bytes → Blob
const binary = atob(content)
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0))
const blob = new Blob([bytes], { type: mimeType })
// Trigger browser download
const url = URL.createObjectURL(blob)
const a = Object.assign(document.createElement('a'), { href: url, download: filename })
a.click()
URL.revokeObjectURL(url)
}
await downloadDecodedFile('/api/reports/latest', sessionStorage.getItem('auth_token')!)Node.js とシェルでのコマンドライン Base64 デコード
CI/CD スクリプト、デバッグセッション、または一度限りのデコードタスクには、 シェルツールや Node.js ワンライナーの方が完全なスクリプトより速いです。 macOS と Linux でフラグ名が異なることに注意してください。
# ── macOS / Linux system base64 ───────────────────────────────────────
# Standard decoding (macOS uses -D, Linux uses -d)
echo "ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=" | base64 -d # Linux
echo "ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=" | base64 -D # macOS
# Decode a .b64 file to its original binary
base64 -d ./dist/cert.b64 > ./ssl/server.crt # Linux
base64 -D -i ./dist/cert.b64 -o ./ssl/server.crt # macOS
# URL-safe Base64 — restore + and / before decoding
echo "eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ" | tr '-_' '+/' | base64 -d
# ── Node.js one-liner — works on Windows too ───────────────────────────
node -e "process.stdout.write(Buffer.from(process.argv[1], 'base64').toString())" "ZGVwbG95LWJvdA=="
# deploy-bot
# URL-safe (Node.js 18+)
node -e "process.stdout.write(Buffer.from(process.argv[1], 'base64url').toString())" "eyJhbGciOiJIUzI1NiJ9"
# {"alg":"HS256"}base64 のデコードに -D(大文字)を使いますが、Linux は -d(小文字)を使います。 これにより CI スクリプトがサイレントに失敗します — ターゲットプラットフォームが Linux である保証がない場合は Node.js ワンライナーを使用してください。高性能な代替手段:js-base64
ライブラリを選ぶ主な理由はクロス環境の一貫性です。ブラウザと Node.js の両方で バンドラー設定なしに動作するパッケージを公開する場合、Buffer は環境検出が必要で、atob() は TextDecoder の回避策が必要です。js-base64(npm 週間ダウンロード数 1 億以上)は 両方を透過的に処理します。
npm install js-base64 # or pnpm add js-base64
import { fromBase64, fromBase64Url, isValid } from 'js-base64'
// Standard decoding — Unicode-safe, works in browser and Node.js
const raw = fromBase64('eyJldmVudElkIjoiZXZ0XzdjM2E5ZjFiMmQiLCJ0eXBlIjoiY2hlY2tvdXRfY29tcGxldGVkIiwiY3VycmVuY3kiOiJFVVIiLCJhbW91bnQiOjE0OTAwfQ==')
const event = JSON.parse(raw)
console.log(event.type) // checkout_completed
console.log(event.currency) // EUR
// URL-safe decoding — no manual character replacement needed
const jwtPayload = fromBase64Url('eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciJ9')
const claims = JSON.parse(jwtPayload)
console.log(claims.role) // editor
// Validate before decoding untrusted input
const untrusted = 'not!valid@base64#'
if (!isValid(untrusted)) {
console.error('Rejected: invalid Base64 input')
}
// Binary output — second argument true returns Uint8Array
const pngBytes = fromBase64('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', true)
console.log(pngBytes instanceof Uint8Array) // trueシンタックスハイライト付きターミナル出力
CLI デバッグツールや検査スクリプトを書くとき、大きな JSON ペイロードに対して プレーンな console.log 出力は読みにくいです。chalk(ターミナル着色で最もダウンロードされる npm パッケージ)と Base64 デコードを組み合わせると、読みやすくスキャンしやすいターミナル出力が得られます — JWT 検査、API レスポンスのデバッグ、設定監査に役立ちます。
npm install chalk # chalk v5+ is ESM-only — use import, not require
import chalk from 'chalk'
// Decode and display any Base64 value with smart type detection
function inspectBase64(encoded: string, label = 'Decoded value'): void {
let decoded: string
try {
decoded = Buffer.from(encoded.trim(), 'base64').toString('utf8')
} catch {
console.error(chalk.red('✗ Invalid Base64 input'))
return
}
console.log(chalk.bold.cyan(`\n── ${label} ──`))
// Attempt JSON pretty-print
try {
const parsed = JSON.parse(decoded)
console.log(chalk.green('Type:'), chalk.yellow('JSON'))
for (const [key, value] of Object.entries(parsed)) {
const display = typeof value === 'object' ? JSON.stringify(value) : String(value)
console.log(chalk.green(` ${key}:`), chalk.white(display))
}
return
} catch { /* not JSON */ }
// Plain text fallback
console.log(chalk.green('Type:'), chalk.yellow('text'))
console.log(chalk.white(decoded))
}
// Inspect a Base64-encoded JWT payload
const tokenParts = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsImV4cCI6MTcxNzIwMzYwMH0.SIGNATURE'.split('.')
inspectBase64(tokenParts[0], 'JWT Header')
inspectBase64(tokenParts[1], 'JWT Payload')
// ── JWT Header ──
// Type: JSON
// alg: HS256
// typ: JWT
//
// ── JWT Payload ──
// Type: JSON
// userId: usr_9f2a1c3e8b4d
// role: editor
// exp: 1717203600Node.js ストリームを使った大容量 Base64 ファイルのデコード
Base64 エンコードされたファイルが約 50 MB を超える場合、readFileSync() を使ってメモリ全体に読み込むと問題が生じます。 Node.js ストリームを使うとデータをチャンクでデコードできますが — Base64 はチャンク境界でのパディングエラーを避けるために、 チャンクごとに 4 文字の倍数が必要です(4 文字グループがちょうど 3 バイトにデコードされます)。
import { createReadStream, createWriteStream } from 'node:fs'
import { pipeline } from 'node:stream/promises'
async function streamDecodeBase64(inputPath: string, outputPath: string): Promise<void> {
const readStream = createReadStream(inputPath, { encoding: 'utf8', highWaterMark: 4 * 1024 * 192 })
const writeStream = createWriteStream(outputPath)
let buffer = ''
await pipeline(
readStream,
async function* (source) {
for await (const chunk of source) {
buffer += (chunk as string).replace(/\s/g, '') // strip any whitespace/newlines
// Decode only complete 4-char groups to avoid mid-stream padding issues
const remainder = buffer.length % 4
const safe = buffer.slice(0, buffer.length - remainder)
buffer = buffer.slice(buffer.length - remainder)
if (safe.length > 0) yield Buffer.from(safe, 'base64')
}
if (buffer.length > 0) yield Buffer.from(buffer, 'base64')
},
writeStream,
)
}
// Decode a 200 MB video that was stored as Base64
await streamDecodeBase64('./uploads/product-demo.b64', './dist/product-demo.mp4')
console.log('Stream decode complete')4 × 1024 × 192 = 786,432 文字(768 KB)を使用しています。 50 MB 未満のファイルには、 readFile() + Buffer.from(content.trim(), 'base64') の方がシンプルで十分高速です。よくある間違い
以下の 4 つの間違いは JavaScript コードベースで繰り返し見られます — 非 ASCII 文字や行折り返しされた API レスポンスが本番環境のデコードパスに到達するまで 隠れていることが多いです。
間違い 1 — UTF-8 コンテンツに TextDecoder なしで atob() を使う
問題: atob() は各文字が 1 つの生のバイト値を表すバイナリ文字列を返します。 UTF-8 マルチバイトシーケンス(キリル文字、CJK、アクセント付き文字)は 文字化けした Latin-1 文字として表示されます。 修正: 出力を TextDecoder でラップします。
// ❌ atob() returns the raw UTF-8 bytes as a Latin-1 string const encoded = '0JDQu9C10LrRgdC10Lkg0JjQstCw0L3QvtCy' const decoded = atob(encoded) console.log(decoded) // "Алексей Р˜РІР°РЅРѕРІ" ← wrong
// ✅ Use TextDecoder to correctly interpret the UTF-8 bytes const encoded = '0JDQu9C10LrRgdC10Lkg0JjQstCw0L3QvtCy' const binary = atob(encoded) const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0)) const decoded = new TextDecoder().decode(bytes) console.log(decoded) // Алексей Иванов ✓
間違い 2 — URL セーフ Base64 を直接 atob() に渡す
問題: JWT セグメントは + と / の代わりに - と _ を使い、パディングがありません。atob() は誤った結果を返すかスローする可能性があります。 修正: 先に標準文字を復元してパディングを追加します。
// ❌ URL-safe JWT segment passed directly — unreliable const jwtPayload = 'eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ' const decoded = atob(jwtPayload) // May produce wrong result or throw
// ✅ Restore standard Base64 chars and padding first
function decodeBase64Url(input: string): string {
const b64 = input.replace(/-/g, '+').replace(/_/g, '/')
const pad = b64 + '==='.slice(0, (4 - b64.length % 4) % 4)
const bin = atob(pad)
const bytes = Uint8Array.from(bin, ch => ch.charCodeAt(0))
return new TextDecoder().decode(bytes)
}
const decoded = decodeBase64Url('eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ')
// {"userId":"usr_9f2a1c3e"} ✓間違い 3 — 行折り返しされた Base64 の改行を除去しない
問題: GitHub Contents API と MIME エンコーダーは Base64 出力を 1 行 60〜76 文字で折り返します。atob() は \n 文字で InvalidCharacterError をスローします。 修正: デコード前にすべての空白を除去します。
// ❌ GitHub API content field contains embedded newlines const data = await res.json() const decoded = atob(data.content) // ❌ throws InvalidCharacterError
// ✅ Strip newlines (and any other whitespace) before decoding const data = await res.json() const clean = data.content.replace(/\s/g, '') const decoded = atob(clean) // ✓
間違い 4 — デコードされたバイナリコンテンツに .toString() を呼び出す
問題: 元のデータがバイナリ(画像、PDF、音声)の場合、.toString('utf8') を呼び出すと 認識できないバイトシーケンスを U+FFFD に置き換え、 出力をサイレントに破損させます。 修正: 結果を Buffer として保持します — 文字列に変換しないでください。
// ❌ .toString('utf8') corrupts binary content
import { readFileSync, writeFileSync } from 'node:fs'
const encoded = readFileSync('./uploads/invoice.b64', 'utf8').trim()
const corrupted = Buffer.from(encoded, 'base64').toString('utf8') // ❌
writeFileSync('./out/invoice.pdf', corrupted) // ❌ unreadable PDF// ✅ Keep the Buffer as binary — do not convert to a string
import { readFileSync, writeFileSync } from 'node:fs'
const encoded = readFileSync('./uploads/invoice.b64', 'utf8').trim()
const binary = Buffer.from(encoded, 'base64') // ✓ raw bytes preserved
writeFileSync('./out/invoice.pdf', binary) // ✓ valid PDFJavaScript Base64 デコードメソッド — クイック比較
| メソッド | UTF-8 出力 | バイナリ出力 | URL セーフ | 対応環境 | インストール |
|---|---|---|---|---|---|
| atob() | ❌ TextDecoder が必要 | ✅ バイナリ文字列 | ❌ 手動で復元 | Browser, Node 16+, Bun, Deno | 不要 |
| TextDecoder + atob() | ✅ UTF-8 | ✅ Uint8Array 経由 | ❌ 手動で復元 | Browser, Node 16+, Deno | 不要 |
| Buffer.from().toString() | ✅ utf8 | ✅ Buffer として保持 | ✅ base64url (Node 18+) | Node.js, Bun | 不要 |
| Uint8Array.fromBase64() (TC39) | ✅ TextDecoder 経由 | ✅ ネイティブ | ✅ alphabet オプション | Chrome 130+, Node 22+ | 不要 |
| js-base64 | ✅ 常に | ✅ Uint8Array | ✅ 組み込み | 全環境 | npm install |
デコードされるコンテンツが ASCII テキストであることが保証されている場合のみ atob() を選択してください。 ブラウザでのユーザー入力や多言語テキストには TextDecoder + atob() を使います。 Node.js サーバーサイドコードには、Buffer が正しいデフォルトです — UTF-8 を自動処理し、バイナリデータをそのまま保持します。 クロス環境ライブラリには、 js-base64 がすべてのエッジケースを解消します。
よくある質問
関連ツール
コードを書かずにワンクリックでデコードするには、Base64 文字列を直接 Base64 Decoder に貼り付けてください — ブラウザで標準と URL セーフモードを即座に処理します。
Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.
Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.