Base64 Decode в JavaScript: atob() и Buffer

·Front-end & Node.js Developer·ПровереноSophie Laurent·Опубликовано

Используйте бесплатный Base64 Decode Online прямо в браузере — установка не требуется.

Попробовать Base64 Decode Online онлайн →

Когда я отлаживаю проблему с аутентификацией в продакшене, первое, за что я берусь, — это Base64 decoder: JWT-пэйлоады, подписи вебхуков и закодированные конфигурационные значения прячутся внутри Base64-строк. JavaScript предоставляет два встроенных подхода к base64-декодированию: atob() (браузер + Node.js 16+) и Buffer.from(encoded, 'base64').toString() (Node.js) — и они ведут себя очень по-разному, когда исходные данные содержат символы Unicode. Для быстрого однократного декодирования без написания кода Base64 Decoder от ToolDeck справится мгновенно прямо в браузере. Это руководство охватывает оба окружения — Node.js 16+ и современные браузеры (Chrome 80+, Firefox 75+, Safari 14+) — с готовыми к продакшену примерами: восстановление UTF-8, URL-безопасные варианты, декодирование JWT, файлов, ответов API, потоков Node.js и четыре ошибки, которые стабильно приводят к искажённому выводу в реальных проектах.

  • atob(encoded) встроен в браузер и доступен в Node.js 16+ глобально, но возвращает бинарную строку — используйте TextDecoder для восстановления UTF-8 текста из любого содержимого выше ASCII.
  • Buffer.from(encoded, "base64").toString("utf8") — идиоматический подход в Node.js, обрабатывающий UTF-8 автоматически без дополнительных шагов.
  • URL-безопасный Base64 (используемый в JWT) заменяет + на -, / на _ и убирает паддинг =. Восстановите их перед вызовом atob() или используйте Buffer.from(encoded, "base64url").toString() в Node.js 18+.
  • Удаляйте пробелы и переносы строк перед декодированием — GitHub Contents API и многие MIME-энкодеры переносят вывод Base64 каждые 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 в браузере.

Before · text
After · text
ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
deploy-bot:sk-prod-a7f2c91e4b3d8

atob() — встроенная функция декодирования для браузеров

atob() (ASCII-to-binary) доступен в браузерах начиная с IE10 и стал глобальной функцией в Node.js 16.0 в рамках инициативы WinterCG по совместимости. Он также работает нативно в Deno, Bun и Cloudflare Workers — без импорта.

Функция возвращает бинарную строку: JavaScript-строку, в которой каждый символ имеет кодовую точку, равную одному значению сырого байта (0–255). Это важно: если исходные данные были UTF-8 текстом, содержащим символы выше U+007F (буквы с диакритикой, кириллица, CJK, эмодзи), возвращаемая строка представляет собой сырую последовательность байт, а не читаемый текст. Используйте TextDecoder, чтобы восстановить его (подробнее в следующем разделе).

Минимальный рабочий пример

JavaScript (browser / Node.js 16+)
// 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

Проверка корректности восстановления данных

JavaScript
// 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() — непонимание типа возвращаемого значения. Когда исходный текст был закодирован в UTF-8 перед Base64, atob() возвращает Latin-1 бинарную строку, а не читаемый текст:

JavaScript
// 'Алексей Иванов' was UTF-8 encoded then Base64 encoded before transmission
const encoded = '0JDQu9C10LrRgdC10Lkg0JjQstCw0L3QvtCy'

// ❌ 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-вывода

JavaScript (browser + Node.js 16+)
// 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 = 'Confirmed: 田中太郎 — São Paulo warehouse, qty: 250'
const encoded   = toBase64(orderNote)
const decoded   = fromBase64(encoded)

console.log(decoded === orderNote) // true
console.log(decoded)
// Confirmed: 田中太郎 — São Paulo warehouse, qty: 250
Примечание:В Node.js пропустите шаг с TextDecoder — используйте Buffer.from(encoded, 'base64').toString('utf8'). Он автоматически интерпретирует декодированные байты как UTF-8 и работает быстрее для больших входных данных.

Buffer.from() в Node.js — полное руководство по декодированию

В Node.js Buffer является идиоматическим API для всех бинарных операций, включая декодирование Base64. Он обрабатывает UTF-8 нативно, возвращает полноценный Buffer (безопасный для бинарных данных), а начиная с Node.js 18 поддерживает сокращение 'base64url' для URL-безопасных вариантов.

Декодирование конфига из переменной окружения

Node.js
// 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-файла

Node.js
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

Асинхронное декодирование с обработкой ошибок

Node.js
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 — справочник параметров

Краткий справочник параметров двух основных нативных API декодирования для использования при написании и проверке кода.

atob(encodedData)

ПараметрТипОбязателенОписание
encodedDatastringДаСтандартная Base64-строка, использующая символы +, /, =. URL-безопасные варианты (-, _) вызывают InvalidCharacterError. Пробелы не допускаются.
Возвращает: бинарную строку — кодовая точка каждого символа равна одному значению сырого байта (0–255). Это не Unicode-строка; передайте её через TextDecoder для восстановления UTF-8 текста.

Buffer.from(input, inputEncoding) / .toString(outputEncoding)

ПараметрТипПо умолчаниюОписание
inputstring | Buffer | TypedArray | ArrayBufferобязателенBase64-закодированная строка для декодирования или буфер с закодированными байтами.
inputEncodingBufferEncoding"utf8"Установите "base64" для стандартного Base64 (RFC 4648 §4) или "base64url" для URL-безопасного Base64 (RFC 4648 §5, Node.js 18+).
outputEncodingstring"utf8"Кодировка для вывода .toString(). Используйте "utf8" для читаемого текста, "binary" для Latin-1 бинарной строки, совместимой с выводом atob().
startinteger0Смещение байта внутри декодированного Buffer, с которого начинать чтение. Передаётся в .toString() как второй аргумент.
endintegerbuf.lengthСмещение байта для остановки чтения (не включительно). Передаётся в .toString() как третий аргумент.
Возвращает: Buffer из .from(). Возвращает строку из .toString(). Оставляйте как Buffer (не вызывайте .toString()), когда декодированное содержимое бинарное — изображения, PDF, аудио.

URL-безопасный Base64 — декодирование JWT и URL-параметров

JWT используют URL-безопасный Base64 (RFC 4648 §5) для всех трёх сегментов. URL-безопасный Base64 заменяет + на - и / на _, а также убирает завершающий = паддинг. Передача этого напрямую в atob() без восстановления даёт некорректный вывод или исключение.

Браузер — восстановление символов и паддинга перед декодированием

JavaScript (browser)
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_3a7f91c2

Node.js 18+ — нативная кодировка 'base64url'

Node.js 18+
// 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

Декодирование Base64 из файлов и ответов API

В продакшен-коде декодирование Base64 чаще всего происходит при работе с внешними API, которые возвращают контент в закодированном виде. В обоих сценариях есть важные нюансы вокруг пробелов и бинарного vs текстового вывода. Если вам просто нужно осмотреть закодированный ответ при отладке, вставьте его напрямую в Base64 Decoder — он моментально обрабатывает стандартный и URL-безопасный режимы.

Декодирование содержимого из GitHub Contents API

JavaScript
// 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}`)

Декодирование Base64-закодированного бинарного файла из API (браузер)

JavaScript (browser)
// 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')!)

Декодирование Base64 через командную строку в Node.js и Shell

Для CI/CD-скриптов, отладочных сессий или разовых задач декодирования утилиты командной строки и однострочники Node.js быстрее полноценного скрипта. Обратите внимание, что имя флага различается на macOS и Linux.

bash
# ── 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"}
Примечание:На macOS команда base64 использует -D для декодирования (заглавная D), тогда как Linux использует -d (строчная). Это незаметно ломает CI-скрипты — используйте однострочник Node.js, когда целевая платформа не гарантированно является Linux.

Высокопроизводительная альтернатива: js-base64

Главная причина использовать библиотеку — кросс-средовая согласованность. Если вы поставляете пакет, работающий как в браузере, так и в Node.js без настройки бандлера, Buffer требует определения среды, а atob() требует обходного пути через TextDecoder. js-base64 (более 100 млн еженедельных загрузок npm) прозрачно обрабатывает оба варианта.

bash
npm install js-base64
# or
pnpm add js-base64
JavaScript
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-инструментов отладки или скриптов инспекции простой console.log сложно читать для больших JSON-пэйлоадов. chalk (самый скачиваемый npm-пакет для раскраски терминала) в сочетании с декодированием Base64 даёт читаемый, легко просматриваемый вывод — полезен для инспекции JWT, отладки ответов API и аудита конфигов.

bash
npm install chalk
# chalk v5+ is ESM-only — use import, not require
Node.js
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:    1717203600
Примечание:Используйте chalk только для вывода в терминал/CLI — никогда для содержимого, записываемого в файлы, ответы API или агрегаторы логов. ANSI-escape-коды портят не-терминальных потребителей: log-платформы (Datadog, Splunk), парсеры JSON-логов и просмотрщики логов CI отображают их как нечитаемые последовательности символов.

Декодирование больших Base64-файлов с помощью потоков Node.js

Когда Base64-закодированный файл превышает ~50 МБ, загрузка его целиком в память с помощью readFileSync() становится проблемой. Потоки Node.js позволяют декодировать данные по частям — но Base64 требует кратных 4 символам чанков (каждая 4-символьная группа декодируется ровно в 3 байта) во избежание ошибок паддинга на границах чанков.

Node.js
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 символам при чтении Base64-текста, чтобы каждый чанк содержал только полные 4-символьные группы. В примере используется 4 × 1024 × 192 = 786 432 символа (768 КБ). Для файлов до 50 МБ readFile() + Buffer.from(content.trim(), 'base64') проще и достаточно быстро.

Распространённые ошибки

Я видел эти четыре ошибки в JavaScript-кодовых базах снова и снова — они, как правило, остаются незамеченными до тех пор, пока не ASCII символ или перенесённый ответ API не достигает пути декодирования в продакшене.

Ошибка 1 — Использование atob() без TextDecoder для UTF-8 контента

Проблема: atob() возвращает бинарную строку, где каждый символ — это одно значение сырого байта. Многобайтовые UTF-8 последовательности (кириллица, CJK, буквы с диакритикой) отображаются как искажённые Latin-1 символы. Решение: оберните вывод в TextDecoder.

Before · JavaScript
After · JavaScript
// ❌ 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() может вернуть неверные данные или выбросить исключение. Решение: сначала восстановите стандартные символы и добавьте паддинг.

Before · JavaScript
After · JavaScript
// ❌ 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 каждые 60–76 символов. atob() выбрасывает InvalidCharacterError на символах \n. Решение: удалите все пробелы перед декодированием.

Before · JavaScript
After · JavaScript
// ❌ 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 — не преобразовывайте в строку.

Before · JavaScript
After · JavaScript
// ❌ .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 PDF

Методы декодирования Base64 в JavaScript — быстрое сравнение

Метод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✅ нативный✅ опция alphabetChrome 130+, Node 22+Нет
js-base64✅ всегда✅ Uint8Array✅ встроенУниверсальноnpm install

Используйте atob() только когда декодируемое содержимое гарантированно является ASCII-текстом. Для любого пользовательского или многоязычного текста в браузере используйте TextDecoder + atob(). Для серверного кода Node.js Buffer — правильный выбор по умолчанию: он обрабатывает UTF-8 автоматически и сохраняет бинарные данные нетронутыми. Для кросс-средовых библиотек js-base64 устраняет все граничные случаи.

Часто задаваемые вопросы

Почему atob() возвращает искажённые символы вместо читаемого текста?
atob() возвращает бинарную строку, где каждый символ представляет один сырой байт (0–255), а не кодовую точку Unicode. Если исходный текст был закодирован в UTF-8, любой символ выше U+007F — кириллица, арабский, иероглифы CJK, буквы с диакритикой — появится как два или более искажённых Latin-1 символа. Решение: передайте вывод через TextDecoder: const bytes = Uint8Array.from(atob(encoded), ch => ch.charCodeAt(0)); const text = new TextDecoder().decode(bytes). В Node.js используйте Buffer.from(encoded, 'base64').toString('utf8') — он обрабатывает это автоматически.
Как декодировать JWT-токен в JavaScript?
JWT состоит из трёх URL-безопасных Base64-сегментов, разделённых точками: header.payload.signature. Для декодирования пэйлоада: const [, payloadB64] = token.split('.'). В браузере: восстановите стандартные символы, добавьте паддинг, декодируйте с помощью atob() и TextDecoder. В Node.js 18+: Buffer.from(payloadB64, 'base64url').toString('utf8'). Важно: декодирование только раскрывает claims — оно НЕ верифицирует подпись. Используйте специализированную JWT-библиотеку (jsonwebtoken, jose) для верифицированного декодирования в продакшене.
В чём разница между atob() и Buffer.from() для декодирования?
atob() доступен во всех JavaScript-окружениях (браузер, Node.js 16+, Bun, Deno) без импортов, но возвращает бинарную строку — для конвертации UTF-8 контента в читаемый текст нужен TextDecoder. Buffer.from(encoded, 'base64') работает только в Node.js / Bun, возвращает настоящий Buffer (безопасный для бинарных данных), нативно обрабатывает UTF-8 и поддерживает 'base64url' в Node.js 18+. Для серверного кода Buffer проще. Для браузерного кода стандарт — atob() + TextDecoder. Для кросс-средовых библиотек js-base64 абстрагирует разницу.
Как декодировать URL-безопасный Base64 в браузере?
URL-безопасный Base64 заменяет + на -, / на _ и убирает паддинг =. Восстановите их перед вызовом atob(): const b64 = input.replace(/-/g, '+').replace(/_/g, '/'); const padded = b64 + '==='.slice(0, (4 - b64.length % 4) % 4); const text = new TextDecoder().decode(Uint8Array.from(atob(padded), c => c.charCodeAt(0))). В Node.js 18+: Buffer.from(input, 'base64url').toString('utf8') обрабатывает это одним вызовом.
Как декодировать Base64-контент из GitHub API в JavaScript?
GitHub Contents API возвращает содержимое файла как стандартный Base64 с символами переноса строки каждые 60 символов. Удалите их перед декодированием: const clean = data.content.replace(/\n/g, ''). В браузере: new TextDecoder().decode(Uint8Array.from(atob(clean), c => c.charCodeAt(0))). В Node.js: Buffer.from(clean, 'base64').toString('utf8'). Для бинарных файлов (изображения, PDF) оставляйте Buffer без вызова .toString() — передавайте его напрямую в writeFile или поток ответа.
Можно ли декодировать Base64-изображение в JavaScript без библиотеки?
Да. В браузере: const binary = atob(encoded); const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0)); const blob = new Blob([bytes], { type: 'image/png' }); const url = URL.createObjectURL(blob). Для src тега img постройте data URI: const src = 'data:image/png;base64,' + encoded — это полностью пропускает шаг декодирования. В Node.js: Buffer.from(encoded, 'base64'), затем writeFileSync('./out.png', buffer). Главное правило: никогда не вызывайте .toString() на декодированном Buffer, если содержимое бинарное.

Связанные инструменты

Для декодирования в один клик без написания кода вставьте вашу Base64-строку напрямую в Base64 Decoder — он обрабатывает стандартный и URL-безопасный режимы с мгновенным выводом в браузере.

Также доступно на:PythonGoJavaC#
AC
Alex ChenFront-end & Node.js Developer

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.

SL
Sophie LaurentТехнический рецензент

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.