Skip to main content
Back to blog

Base64 Encoding Explained: A Practical Guide for QA Engineers

Understand Base64 encoding and decoding — what it is, why APIs use it, and where QA engineers encounter it in authentication headers, JWT tokens, file uploads, and email encoding. Includes practical examples for test automation.

InnovateBits7 min read
Share

Base64 appears in almost every API you'll test, often without you realising it. HTTP Basic Auth headers, JWT tokens, file upload endpoints, email content encoding, and binary data in JSON responses — Base64 is the common thread connecting all of them. Understanding what it does (and what it doesn't do) will save you significant debugging time.


What Base64 actually does

Base64 is an encoding scheme that converts binary data into a string of 64 printable ASCII characters: A–Z, a–z, 0–9, +, and /. The = character is used for padding.

It is not encryption. It is not compression. It is a reversible transformation that makes binary data safe to embed in text-based protocols.

The name "Base64" comes from the mathematical base: each character represents 6 bits (2^6 = 64 values). Since bytes are 8 bits, every 3 bytes of input become 4 Base64 characters. This means Base64-encoded data is always approximately 33% larger than the original.

Original:  Man         (3 bytes: 0x4D 0x61 0x6E)
Base64:    TWFu        (4 characters)

Original:  Ma          (2 bytes — padded to 3)
Base64:    TWE=        (4 characters, one = padding)

Original:  M           (1 byte — padded to 3)
Base64:    TQ==        (4 characters, two == padding)

Where QA engineers encounter Base64

1. HTTP Basic Authentication

The oldest and most common use: Basic Auth encodes username:password as Base64 and sends it in the Authorization header.

Authorization: Basic dXNlcjpwYXNzd29yZA==

Decode it:

dXNlcjpwYXNzd29yZA== → user:password

When testing an API that uses Basic Auth, you'll need to construct this header in your test:

function basicAuthHeader(username: string, password: string): string {
  return 'Basic ' + btoa(`${username}:${password}`)
}
 
const response = await request.get('/api/admin/stats', {
  headers: {
    Authorization: basicAuthHeader('admin', 'secret123')
  }
})
expect(response.status()).toBe(200)

Security testing point: the password is only encoded, not encrypted. Basic Auth over HTTP is completely insecure — the credentials are trivially recoverable from a network capture. Always assert that your API enforces HTTPS and returns 400/403 over plain HTTP.

2. JWT tokens

As discussed in the JWT Decoder article, each part of a JWT is Base64URL-encoded. Base64URL is a variant that replaces + with - and / with _, and omits the = padding — making the result safe to use in URLs and HTTP headers without percent-encoding.

// Decode a JWT payload (without verification)
function decodeJwtPayload(token: string): Record<string, unknown> {
  const payloadB64 = token.split('.')[1]
  // Restore standard Base64 padding
  const padded = payloadB64 + '='.repeat((4 - payloadB64.length % 4) % 4)
  return JSON.parse(atob(padded.replace(/-/g, '+').replace(/_/g, '/')))
}

3. File upload APIs

Many APIs that accept images, documents, or other files expect the binary content encoded as Base64 within a JSON request body:

{
  "filename": "avatar.png",
  "content_type": "image/png",
  "data": "iVBORw0KGgoAAAANSUhEUgAA..."
}

In Playwright tests, you can read a file from disk and Base64-encode it:

import { readFileSync } from 'fs'
 
const fileBuffer = readFileSync('./tests/fixtures/test-avatar.png')
const base64Data = fileBuffer.toString('base64')
 
const response = await request.post('/api/users/avatar', {
  data: {
    filename: 'avatar.png',
    content_type: 'image/png',
    data: base64Data
  }
})
expect(response.status()).toBe(200)

4. Email headers (MIME encoding)

When testing email delivery (welcome emails, password reset emails, notifications), you may inspect the raw email source. Long subject lines and non-ASCII content are often encoded using Base64 or Quoted-Printable:

Subject: =?UTF-8?B?VGVzdCBzdWJqZWN0IGhlcmU=?=

The B indicates Base64; decode VGVzdCBzdWJqZWN0IGhlcmU= to get Test subject here.

5. API keys and credentials in CI

Some teams store JSON credentials (service account keys, API configurations) as Base64-encoded strings in environment variables to avoid multi-line string issues in CI systems:

# Encode
echo '{"api_key":"secret","environment":"production"}' | base64
 
# Decode in test setup
const creds = JSON.parse(Buffer.from(process.env.API_CREDENTIALS_B64!, 'base64').toString())

Standard vs URL-safe Base64

There are two variants you'll encounter:

FeatureStandard Base64URL-safe Base64
Character 62+-
Character 63/_
Padding=Omitted (or optional)
Used inHTTP body, file content, emailURLs, JWT, OAuth, cookies

The + and / characters in standard Base64 have special meanings in URLs (+ means space, / separates path segments), which is why the URL-safe variant exists.

If you paste standard Base64 into a URL without escaping it, the + characters will be decoded as spaces by the server — causing subtle decoding failures that are hard to diagnose.

// Safe encoding for URL parameters
const urlSafe = btoa(value)
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=/g, '')  // strip padding
 
// Decoding URL-safe Base64
function decodeUrlSafe(s: string): string {
  const padded = s + '='.repeat((4 - s.length % 4) % 4)
  return atob(padded.replace(/-/g, '+').replace(/_/g, '/'))
}

Common Base64 bugs to test for

Double-encoding

When a value gets Base64-encoded twice — usually because one layer of code encodes it and another layer encodes it again — you end up with a string that decodes to another Base64 string rather than the original value.

Test: decode the output and verify it matches the original input, not another Base64 string.

const original = 'hello world'
const encoded  = btoa(original)          // aGVsbG8gd29ybGQ=
const decoded  = atob(encoded)           // hello world
 
expect(decoded).toBe(original)           // ✓
expect(decoded).not.toMatch(/^[A-Za-z0-9+/]+=*$/) // not still Base64

Missing or incorrect padding

Base64 strings must have a length that is a multiple of 4. When padding (=) is stripped (as in JWT and URL-safe contexts), you need to re-add it before decoding. Missing padding produces a DOMException: Failed to execute 'atob' error in browsers.

function safeDecode(b64: string): string {
  const pad = b64.length % 4
  const padded = pad ? b64 + '='.repeat(4 - pad) : b64
  return atob(padded)
}

Character set mismatch

Using standard Base64 characters in a URL context (or vice versa) without conversion will produce incorrect output. Always check which variant an endpoint expects before encoding your test data.

Binary vs text encoding

Base64 is designed for binary data. When encoding Unicode text, you need to first convert to UTF-8 bytes, not just use btoa directly (which only handles Latin-1 characters):

// Wrong — fails for non-Latin-1 characters like emoji or CJK
const wrong = btoa('Héllo')  // throws in some environments
 
// Correct — encode to UTF-8 first
const correct = btoa(
  encodeURIComponent('Héllo').replace(/%([0-9A-F]{2})/g,
    (_, hex) => String.fromCharCode(parseInt(hex, 16))
  )
)

Using the InnovateBits Base64 tool

The Base64 Encoder & Decoder tool handles both encoding and decoding, supports standard and URL-safe variants, and can encode binary files directly via file upload. This is useful when:

  • Debugging an API that's returning garbled content because of an encoding mismatch
  • Constructing Basic Auth headers for test requests without opening a terminal
  • Checking what credentials are embedded in an Authorization header during a manual testing session
  • Encoding a test fixture file to include in a JSON request body

The Swap button reverses the direction so you can quickly check round-trip fidelity — encode something, then decode the result, and verify you get back the original.


Quick reference: Base64 in HTTP

ContextVariantWhere it appears
Basic AuthStandardAuthorization: Basic {b64(user:pass)}
Bearer JWTURL-safeAuthorization: Bearer {header}.{payload}.{sig}
File upload in JSONStandardRequest body "data": "{b64(file_bytes)}"
Inline images (HTML)Standard<img src="data:image/png;base64,{b64}">
MIME emailStandardBody or attachment content
OAuth state parameterURL-safeQuery string ?state={b64url}
Cookie valuesURL-safeSet-Cookie: session={b64url}
Free newsletter

Stay ahead in AI-driven QA

Get practical tutorials on test automation, AI testing, and quality engineering — straight to your inbox. No spam, unsubscribe any time.

Discussion

Sign in with GitHub to comment · powered by Giscus