Back to Blog

    What is Base64 Encoding? Complete Guide with Examples & When to Use It

    Base64 encoding is one of the most widely used data-encoding schemes in software development — yet it is frequently misunderstood, misused, or confused with encryption. Whether you are embedding images in HTML, decoding a JWT token, or sending binary data over a REST API, understanding Base64 is an essential skill for every developer. This comprehensive guide explains what Base64 is, exactly how the algorithm works, when you should (and should not) use it, and provides ready-to-run code examples in JavaScript, Node.js, and Python.

    What is Base64 Encoding?

    Base64 encoding is a binary-to-text encoding scheme that represents binary data using only 64 printable ASCII characters. The name comes directly from the number 64 — the size of its character alphabet. Its primary purpose is to allow binary data (images, files, arbitrary bytes) to be safely transported through systems that were originally designed to handle only plain text, such as email servers, HTTP headers, and JSON payloads.

    Base64 was formally defined in RFC 2045 (1996) as part of the MIME (Multipurpose Internet Mail Extensions) specification, but its roots go back to the early days of the internet when email systems could only reliably transmit 7-bit ASCII text. Sending a binary file like an image directly through an email server would corrupt the data, because servers were allowed to strip the 8th bit of every byte or convert certain byte sequences (like carriage returns). Base64 solved this by converting every group of 3 bytes into 4 printable ASCII characters — characters that every system understood and would never modify.

    Today Base64 is defined in RFC 4648 and appears in dozens of internet standards: HTTP Basic Authentication, JWT tokens, data URIs, PEM certificates, MIME email attachments, and more. It is not a compression algorithm, and it is not encryption — it is purely a way to represent arbitrary bytes using a safe, limited set of text characters.

    How Base64 Encoding Works

    The Base64 algorithm is elegantly simple. At a high level, it takes every 3 bytes (24 bits) of input and converts them into 4 Base64 characters (24 bits), each representing 6 bits of the original data. Here is the step-by-step process:

    • Step 1 — Take 3 bytes: Read 3 bytes (24 bits) of binary input at a time. For example, the ASCII string Man is bytes 0x4D 0x61 0x6E which in binary is 01001101 01100001 01101110.
    • Step 2 — Split into 4 groups of 6 bits: Concatenate the 24 bits and split into four 6-bit groups: 010011 | 010110 | 000101 | 101110, giving decimal values 19, 22, 5, 46.
    • Step 3 — Map to Base64 characters: Use the Base64 alphabet table to map each 6-bit value to a character. 19 → T, 22 → W, 5 → F, 46 → u. So Man encodes to TWFu.
    • Step 4 — Handle padding: If the input length is not a multiple of 3, the encoder pads the output with one or two = characters to make the total output length a multiple of 4. This allows decoders to know exactly how many bytes the original data contained.

    Let's trace through a complete example. Encoding the string "Hello" (5 bytes):

    Input:    H        e        l        l        o
    Bytes:    0x48     0x65     0x6C     0x6C     0x6F
    Binary:   01001000 01100101 01101100 01101100 01101111
    
    Group 1 (bytes 1-3): 01001000 01100101 01101100
      6-bit groups: 010010 | 000110 | 010101 | 101100
      Indices:      18     | 6      | 21     | 44
      Chars:        S      | G      | V      | s
    
    Group 2 (bytes 4-5 + padding): 01101100 01101111 00000000 (padded)
      6-bit groups: 011011 | 000110 | 111100 | (pad)
      Indices:      27     | 6      | 60
      Chars:        b      | G      | 8      | =
    
    Result: "SGVsbG8="

    The trailing = tells the decoder that one byte of padding was added, meaning the last group contained only 2 real bytes. Two == means only 1 real byte was in the last group.

    Base64 Character Table

    The standard Base64 alphabet consists of exactly 64 characters plus the = padding character:

    Index RangeCharactersCountDescription
    0 – 25A – Z26Uppercase Latin letters
    26 – 51a – z26Lowercase Latin letters
    52 – 610 – 910Decimal digits
    62+1Plus sign (standard Base64)
    63/1Forward slash (standard Base64)
    Padding=1Padding character (not part of alphabet)

    These 64 characters were chosen because they are safe ASCII characters that every legacy text-handling system would preserve without modification. Notice that characters like <, >, &, ", and whitespace are deliberately excluded because they have special meaning in HTML, XML, URLs, and email headers.

    There is also a variant called Base64URL (defined in RFC 4648 §5), which replaces + with - and / with _ to make the output safe for use in URLs and filenames without percent-encoding. This is the variant used in JWT tokens and many modern web APIs.

    When Should You Use Base64?

    Base64 shines whenever you need to safely embed or transmit binary data through a text-only channel. Here are the most important real-world use cases:

    1. Embedding Images in HTML and CSS (Data URIs)

    Instead of referencing an external image file, you can embed it directly in your HTML or CSS using a data URI. This eliminates an HTTP request and is particularly useful for small icons, inline SVGs, and loading spinners.

    <!-- Embedding a PNG image directly in HTML -->
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." alt="logo" />
    
    /* Or in CSS */
    .icon {
      background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cD...");
    }

    2. JWT (JSON Web Tokens)

    JSON Web Tokens use Base64URL encoding for all three of their parts: the header, payload, and signature. Because JWT is transmitted in HTTP headers and URLs, the URL-safe variant of Base64 (- and _ instead of + and /) is used. You can decode any JWT payload by simply Base64-decoding the middle segment.

    // Example JWT (3 Base64URL-encoded parts separated by dots):
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
      .eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0
      .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    
    // Decoding the payload (middle part):
    atob("eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0")
    // → '{"sub":"1234567890","name":"John Doe"}'

    3. HTTP Basic Authentication

    The HTTP Authorization header for Basic Auth encodes credentials as Base64. The browser or client concatenates username:password, Base64-encodes the result, and sends it in the header. This is not encryption — it is just encoding — which is why HTTPS is required for Basic Auth.

    // Encoding "admin:secret" for HTTP Basic Auth
    btoa("admin:secret")  // → "YWRtaW46c2VjcmV0"
    
    // The resulting HTTP header:
    Authorization: Basic YWRtaW46c2VjcmV0

    4. Email Attachments (MIME)

    The original use case for Base64 — email systems use MIME encoding to attach binary files (PDFs, images, executables) to email messages. The file is Base64-encoded and placed in the email body as text, then decoded by the recipient's email client.

    5. API Credentials and Configuration Files

    Many services encode API keys, certificates, and secrets as Base64 in environment variables and config files. This is common in Kubernetes secrets, CI/CD pipelines (GitHub Actions, CircleCI), and cloud provider SDKs where binary certificate data must be stored as a string.

    # Kubernetes secret (values are Base64-encoded)
    apiVersion: v1
    kind: Secret
    metadata:
      name: my-secret
    data:
      username: YWRtaW4=       # "admin" base64-encoded
      password: c2VjcmV0cGFzcw== # "secretpass" base64-encoded

    6. Storing Binary Data in JSON / Databases

    JSON does not natively support binary data. If you need to include a file, image, or cryptographic key in a JSON payload, Base64 encoding is the standard approach — used by Google Cloud APIs, AWS, and many REST APIs worldwide.

    When NOT to Use Base64

    Base64 is frequently misused. Here is when you should avoid it:

    • Never use it for encryption or security. Base64 is completely reversible with zero secret — anyone can decode it instantly. It provides no confidentiality whatsoever. If you need to protect data, use proper encryption (AES, RSA) or hashing (bcrypt, SHA-256).
    • Avoid it for large file transfers. Base64 inflates data size by approximately 33% (every 3 bytes become 4 characters). Transferring large images or files as Base64 through an API wastes bandwidth significantly compared to sending the raw binary via multipart form data or a presigned URL.
    • Don't use it as a compression algorithm. Base64 makes data larger, not smaller. If storage or bandwidth is a concern, use compression (gzip, Brotli) before Base64 encoding, not instead of it.
    • Avoid embedding very large images as data URIs. While data URIs are convenient for small icons, embedding large images in HTML/CSS bloats your document size, prevents browser caching of the image (since it is part of the HTML/CSS), and can hurt page load performance.
    • Don't rely on it to sanitize user input. Some developers mistakenly Base64-encode user input thinking it prevents XSS or injection attacks. It does not — the data must still be properly validated and sanitized after decoding.

    Base64 Encoding in JavaScript

    Modern browsers expose two global functions for Base64 encoding and decoding: btoa() (binary-to-ASCII, i.e., encode) and atob() (ASCII-to-binary, i.e., decode). These work in all modern browsers and Deno, but have a limitation: they only handle Latin-1 (ISO-8859-1) characters, not arbitrary Unicode strings.

    // Browser / Deno — basic encode & decode
    const encoded = btoa("Hello, World!");
    console.log(encoded); // "SGVsbG8sIFdvcmxkIQ=="
    
    const decoded = atob("SGVsbG8sIFdvcmxkIQ==");
    console.log(decoded); // "Hello, World!"
    
    // ⚠️ Limitation: btoa() throws for non-Latin1 characters
    // btoa("Hello 🌍") → DOMException: The string to be encoded
    //   contains characters outside of the Latin1 range.
    
    // ✅ Solution: encode Unicode strings properly
    function encodeUnicode(str) {
      return btoa(
        encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, p1) =>
          String.fromCharCode(parseInt(p1, 16))
        )
      );
    }
    
    function decodeUnicode(str) {
      return decodeURIComponent(
        atob(str)
          .split("")
          .map((c) => "%" + c.charCodeAt(0).toString(16).padStart(2, "0"))
          .join("")
      );
    }
    
    encodeUnicode("Hello 🌍"); // "SGVsbG8g8J+MjQ=="
    decodeUnicode("SGVsbG8g8J+MjQ=="); // "Hello 🌍"

    Base64 in Node.js (Buffer API)

    Node.js uses the Buffer class for Base64 operations, which handles Unicode and binary data correctly without the Latin-1 limitation:

    // Node.js — encoding strings
    const encoded = Buffer.from("Hello, World!").toString("base64");
    console.log(encoded); // "SGVsbG8sIFdvcmxkIQ=="
    
    // Node.js — decoding
    const decoded = Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64").toString("utf8");
    console.log(decoded); // "Hello, World!"
    
    // Node.js — encoding a file to Base64
    const fs = require("fs");
    const imageBuffer = fs.readFileSync("./logo.png");
    const imageBase64 = imageBuffer.toString("base64");
    const dataURI = `data:image/png;base64,${imageBase64}`;
    
    // Node.js — Base64URL encoding (safe for URLs)
    const base64url = Buffer.from("Hello, World!")
      .toString("base64")
      .replace(/+/g, "-")
      .replace(///g, "_")
      .replace(/=/g, ""); // strip padding for URL use
    console.log(base64url); // "SGVsbG8sIFdvcmxkIQ"
    
    // Node.js v16+ — native base64url support
    const base64urlNative = Buffer.from("Hello, World!").toString("base64url");
    console.log(base64urlNative); // "SGVsbG8sIFdvcmxkIQ"

    Base64 Encoding in Python

    Python's standard library includes the base64 module, which provides functions for standard Base64, Base64URL, and several other variants. Note that Python's Base64 functions work on bytes objects, not plain strings, so you need to encode strings first.

    import base64
    
    # Encoding a string to Base64
    text = "Hello, World!"
    encoded_bytes = base64.b64encode(text.encode("utf-8"))
    encoded_str = encoded_bytes.decode("utf-8")
    print(encoded_str)  # SGVsbG8sIFdvcmxkIQ==
    
    # Decoding a Base64 string
    decoded_bytes = base64.b64decode("SGVsbG8sIFdvcmxkIQ==")
    decoded_str = decoded_bytes.decode("utf-8")
    print(decoded_str)  # Hello, World!
    
    # Encoding a file to Base64
    with open("logo.png", "rb") as image_file:
        image_base64 = base64.b64encode(image_file.read()).decode("utf-8")
    data_uri = f"data:image/png;base64,{image_base64}"
    
    # URL-safe Base64 (Base64URL) — replaces + with - and / with _
    url_safe_encoded = base64.urlsafe_b64encode(text.encode("utf-8"))
    print(url_safe_encoded.decode("utf-8"))  # SGVsbG8sIFdvcmxkIQ==
    
    url_safe_decoded = base64.urlsafe_b64decode("SGVsbG8sIFdvcmxkIQ==")
    print(url_safe_decoded.decode("utf-8"))  # Hello, World!
    
    # Handling missing padding (common when decoding JWT segments)
    def decode_base64_url(data: str) -> bytes:
        # Add back padding stripped by JWT
        padding = 4 - len(data) % 4
        data += "=" * (padding % 4)
        return base64.urlsafe_b64decode(data)
    
    payload = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0"
    print(decode_base64_url(payload))
    # b'{"sub":"1234567890","name":"John Doe"}'

    Base64 vs Hex Encoding: Which Should You Use?

    Both Base64 and hexadecimal (hex) encoding are binary-to-text schemes, but they serve different purposes and have different trade-offs. Here is a detailed comparison:

    PropertyBase64Hex
    Alphabet size64 characters16 characters (0–9, a–f)
    Bits per character6 bits4 bits
    Output size overhead~33% larger than input~100% larger than input (2× bytes)
    EfficiencyMore compactLess compact
    Human readabilityLow (looks like random text)Medium (easy to parse byte-by-byte)
    URL safetyNo (+ and / must be escaped; use Base64URL)Yes (all hex chars are URL-safe)
    Common use casesImages, JWT, email, binary in JSONHashes (MD5, SHA), color codes, checksums, debugging
    PaddingYes (= character)No
    StandardRFC 4648No formal standard, universally understood

    Rule of thumb: Use Base64 when transmitting binary data through text channels (HTTP, JSON, email). Use hex when displaying cryptographic hashes, checksums, memory addresses, or any value where human readability matters. A SHA-256 hash in hex (2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824) is far more recognizable and debuggable than its Base64 equivalent.

    Real-World Base64 Examples

    Embedding an SVG Icon as a CSS Background

    Small SVG icons can be inlined in CSS to eliminate extra network requests. This is especially useful for critical above-the-fold icons in server-side rendered applications:

    /* Original SVG:
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M12 2L2 7l10 5 10-5-10-5z"/>
    </svg>
    */
    
    /* Base64-encoded data URI in CSS */
    .icon-layers {
      background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEyIDJMMiA3bDEwIDUgMTAtNS0xMC01eiIvPjwvc3ZnPg==");
      width: 24px;
      height: 24px;
      background-size: contain;
      background-repeat: no-repeat;
    }

    Decoding a JWT Token Manually

    A JWT has three parts separated by dots. The header and payload are Base64URL-encoded JSON objects. You can decode them without any library — which is useful for debugging:

    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
                ".eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJBbGljZSIsImlhdCI6MTcxNzI0NjQwMCwiZXhwIjoxNzE3MzMyODAwfQ" +
                ".SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
    
    function decodeJwtPart(base64url) {
      // Convert Base64URL → Base64
      const base64 = base64url
        .replace(/-/g, "+")
        .replace(/_/g, "/")
        .padEnd(base64url.length + (4 - (base64url.length % 4)) % 4, "=");
      return JSON.parse(atob(base64));
    }
    
    const [headerB64, payloadB64] = jwt.split(".");
    const header = decodeJwtPart(headerB64);
    const payload = decodeJwtPart(payloadB64);
    
    console.log(header);
    // { alg: "HS256", typ: "JWT" }
    
    console.log(payload);
    // { sub: "user_123", name: "Alice", iat: 1717246400, exp: 1717332800 }

    Sending an Image via REST API

    Many AI and vision APIs (OpenAI Vision, Google Vision AI) accept images as Base64 strings in their JSON request bodies:

    // Sending an image to OpenAI Vision API
    async function analyzeImage(imagePath) {
      const fs = require("fs");
      const imageData = fs.readFileSync(imagePath);
      const base64Image = imageData.toString("base64");
    
      const response = await fetch("https://api.openai.com/v1/chat/completions", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
        },
        body: JSON.stringify({
          model: "gpt-4o",
          messages: [
            {
              role: "user",
              content: [
                { type: "text", text: "What is in this image?" },
                {
                  type: "image_url",
                  image_url: { url: `data:image/jpeg;base64,${base64Image}` },
                },
              ],
            },
          ],
        }),
      });
      return response.json();
    }

    Frequently Asked Questions (FAQ)

    Is Base64 a form of encryption?

    No. Base64 is not encryption. Encryption requires a secret key and produces output that cannot be reversed without that key. Base64 is a completely reversible encoding that requires no key — anyone can decode Base64 in milliseconds using freely available tools. Never rely on Base64 to protect sensitive data. Use it only to safely transport data through text channels, and use proper encryption (AES, RSA, TLS) for security.

    Why does Base64 output end with = or ==?

    Base64 processes input in groups of 3 bytes and produces groups of 4 characters. When the input length is not a multiple of 3, the final group is incomplete. The = padding character fills in the remaining positions to keep the output aligned to 4-character blocks. One = means the last group had 2 bytes (producing 3 Base64 chars + 1 pad). Two == means the last group had only 1 byte (producing 2 Base64 chars + 2 pads). Padding is optional in some contexts (like Base64URL in JWTs) but required by strict decoders.

    Is Base64 safe to use in URLs?

    Standard Base64 is not URL-safe because the characters +, /, and = have special meaning in URLs and query strings. Placing standard Base64 in a URL without percent-encoding will break or corrupt the data. Always use Base64URL (replacing + with - and / with _) for URL contexts. In Node.js, use Buffer.toString("base64url"); in Python, use base64.urlsafe_b64encode().

    What is Base64URL encoding and how does it differ from regular Base64?

    Base64URL is a variant of Base64 defined in RFC 4648 §5 that is safe for use in URLs, filenames, and HTTP headers without percent-encoding. The only differences are: + is replaced by -, / is replaced by _, and the = padding is typically omitted. The encoding and decoding algorithm is otherwise identical. Base64URL is the standard used in JWT tokens, OAuth 2.0 PKCE challenges, and many modern web APIs. When you see a long string of letters, numbers, hyphens, and underscores in a URL or Authorization header, it is almost certainly Base64URL.

    Conclusion

    Base64 encoding is a foundational tool in a developer's toolkit. It solves a specific, well-defined problem: safely representing arbitrary binary data as printable ASCII text. Understanding when to reach for it — and equally important, when not to — will make you a more effective developer.

    To recap the key takeaways from this guide:

    • Base64 converts every 3 bytes of binary data into 4 printable ASCII characters, producing output ~33% larger than the original input.
    • It is the standard encoding for images in data URIs, JWT tokens, HTTP Basic Authentication, MIME email attachments, Kubernetes secrets, and binary data in JSON APIs.
    • Base64 is not encryption. It provides zero security — anyone can decode it immediately without any key.
    • Use Base64URL (with - and _) for URL contexts and = padding is typically dropped.
    • In JavaScript, use btoa()/atob() for simple strings in browsers, or Buffer in Node.js for full Unicode and binary support.
    • In Python, use the base64 module — b64encode() and b64decode() for standard, or urlsafe_b64encode() for URL-safe variant.
    • Base64 is more space-efficient than hex (33% vs 100% overhead), but hex is more human readable for cryptographic hashes and checksums.

    Whether you are building a REST API, debugging JWT authentication, embedding assets in a web app, or configuring Kubernetes secrets, you will encounter Base64 constantly throughout your career as a developer. Mastering it is time well spent.

    Ready to encode or decode Base64 right now? Try our free online Base64 Encoder & Decoder tool — instantly encode text, images, and files to Base64 or decode any Base64 string back to its original form. No signup required, works entirely in your browser, and supports standard Base64 and Base64URL formats.