When You Actually Need Base64 for a PDF
Base64 is text, PDFs are binary. The main reasons to turn a PDF into Base64:
- JSON API payloads — e-signature services (DocuSign, SignNow, Adobe Sign, HelloSign/Dropbox Sign), document AI APIs, custom webhook endpoints. JSON cannot carry raw binary; Base64 is the ASCII-safe serialisation.
- Inline embedding on a web page — a
data:application/pdf;base64,...URL can be thesrcof an<iframe>,<object>, or<embed>element. Useful for self-contained HTML reports. - Email attachments — SMTP bodies are 7-bit ASCII. Attachments go in as Base64 with Content-Transfer-Encoding: base64 (and MIME line wrap — see our MIME Base64 page).
- Database storage — storing binary in TEXT columns, serializing to logs, or passing through transports that strip bytes outside 0-127.
- CLI pipes — passing PDF bytes through shell pipelines that mangle binary (SSH control channels, chat/webhook payloads).
If none of those apply, keep the PDF as a binary file. Base64 adds 33% to size, costs CPU to encode/decode, and brings zero functional benefit for pure storage or download.
Encoding a PDF in JavaScript (Browser)
// Pattern 1: file input to data URL
const input = document.querySelector('input[type=file]');
input.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
// reader.result is: "data:application/pdf;base64,JVBERi0x..."
const dataUrl = reader.result;
// For API payloads: strip the prefix to get raw Base64
const rawBase64 = dataUrl.split(',')[1];
console.log('data URL:', dataUrl.slice(0, 60) + '...');
console.log('raw Base64:', rawBase64.slice(0, 60) + '...');
};
reader.onerror = () => console.error(reader.error);
reader.readAsDataURL(file);
});
// Pattern 2: fetch result and encode (works with blobs/responses)
async function fetchAndEncode(url) {
const response = await fetch(url);
const blob = await response.blob();
const buffer = await blob.arrayBuffer();
const bytes = new Uint8Array(buffer);
// btoa requires a binary string; chunk to avoid stack overflow on large files
let binary = '';
const chunkSize = 0x8000;
for (let i = 0; i < bytes.length; i += chunkSize) {
binary += String.fromCharCode.apply(
null,
bytes.subarray(i, i + chunkSize)
);
}
return btoa(binary);
}Encoding a PDF in Node.js
import fs from 'node:fs/promises';
import { Buffer } from 'node:buffer';
async function pdfToBase64(path) {
const buf = await fs.readFile(path);
// Single-line Base64 — correct for most APIs
return buf.toString('base64');
}
async function pdfToDataUrl(path) {
const b64 = await pdfToBase64(path);
return 'data:application/pdf;base64,' + b64;
}
// Usage
const b64 = await pdfToBase64('./contract.pdf');
console.log('length:', b64.length);
console.log('preview:', b64.slice(0, 60));
// JVBERi0xLjQKJcfs8vUKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZw...
// Round-trip verification
import crypto from 'node:crypto';
const original = await fs.readFile('./contract.pdf');
const roundTripped = Buffer.from(b64, 'base64');
const originalHash = crypto.createHash('sha256').update(original).digest('hex');
const rtHash = crypto.createHash('sha256').update(roundTripped).digest('hex');
console.log('round-trip OK:', originalHash === rtHash);Encoding a PDF in Python
import base64
from pathlib import Path
# Pattern 1: single-line Base64 for API payloads
def pdf_to_base64(path: str) -> str:
data = Path(path).read_bytes()
return base64.b64encode(data).decode('ascii')
# Pattern 2: data URL for inline embedding
def pdf_to_data_url(path: str) -> str:
return 'data:application/pdf;base64,' + pdf_to_base64(path)
# Pattern 3: MIME-wrapped Base64 for email attachments (76-char lines + CRLF)
def pdf_to_mime_base64(path: str) -> str:
data = Path(path).read_bytes()
# encodebytes inserts \n every 76 chars; swap to CRLF for strict MIME
wrapped = base64.encodebytes(data).decode('ascii')
return wrapped.replace('\n', '\r\n')
# Verify a PDF
b64 = pdf_to_base64('contract.pdf')
print(f'length: {len(b64):,} chars')
print(f'preview: {b64[:60]}...')
# Round-trip
decoded = base64.b64decode(b64)
assert decoded[:5] == b'%PDF-', 'not a valid PDF'
print('PDF magic bytes OK')Encoding a PDF in Java
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
public class PdfToBase64 {
public static void main(String[] args) throws Exception {
byte[] pdfBytes = Files.readAllBytes(Path.of("contract.pdf"));
// Single-line Base64 for API payloads
String b64 = Base64.getEncoder().encodeToString(pdfBytes);
// Data URL for inline HTML embed
String dataUrl = "data:application/pdf;base64," + b64;
// MIME-wrapped for email (76-char CRLF lines)
String mimeB64 = Base64.getMimeEncoder().encodeToString(pdfBytes);
System.out.printf("length: %,d chars%n", b64.length());
System.out.printf("preview: %s...%n", b64.substring(0, 60));
// Round-trip verification
byte[] decoded = Base64.getDecoder().decode(b64);
assert java.util.Arrays.equals(pdfBytes, decoded) : "round-trip mismatch";
System.out.println("round-trip OK");
}
}Encoding a PDF in Go
package main
import (
"encoding/base64"
"fmt"
"os"
)
func pdfToBase64(path string) (string, error) {
data, err := os.ReadFile(path)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(data), nil
}
func pdfToDataURL(path string) (string, error) {
b64, err := pdfToBase64(path)
if err != nil {
return "", err
}
return "data:application/pdf;base64," + b64, nil
}
func main() {
b64, err := pdfToBase64("contract.pdf")
if err != nil {
panic(err)
}
fmt.Printf("length: %d chars\n", len(b64))
fmt.Printf("preview: %s...\n", b64[:60])
// Round-trip
decoded, _ := base64.StdEncoding.DecodeString(b64)
if string(decoded[:5]) == "%PDF-" {
fmt.Println("PDF magic bytes OK")
}
}Encoding a PDF with curl / Bash
# Linux / macOS — single line (strip default wrap with --wrap=0 on GNU, -b 0 on BSD)
B64=$(base64 --wrap=0 contract.pdf 2>/dev/null || base64 -i contract.pdf | tr -d '\n')
echo "length: ${#B64}"
echo "preview: ${B64:0:60}..."
# Build a JSON payload for an API like DocuSign
cat <<JSON > payload.json
{
"emailSubject": "Please sign",
"documents": [{
"documentBase64": "${B64}",
"name": "Contract.pdf",
"fileExtension": "pdf",
"documentId": "1"
}]
}
JSON
# Send to API
curl -X POST \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d @payload.json \
https://api.example.com/envelopes
# Round-trip verification
echo "${B64}" | base64 -d > restored.pdf
diff contract.pdf restored.pdf && echo "round-trip OK"Embedding a PDF Inline on a Web Page
<!-- Pattern 1: iframe with data URL (most compatible) -->
<iframe
src="data:application/pdf;base64,JVBERi0xLjQKJcfs8vUKMSAwIG9iag..."
width="800"
height="600"
style="border:0"
title="Report preview"
></iframe>
<!-- Pattern 2: object element with text fallback -->
<object
data="data:application/pdf;base64,JVBERi0x..."
type="application/pdf"
width="800"
height="600"
>
<p>Your browser cannot display the PDF.
<a href="data:application/pdf;base64,JVBERi0x..." download="report.pdf">
Download it instead.
</a>
</p>
</object>
<!-- Pattern 3: anchor download attribute -->
<a href="data:application/pdf;base64,JVBERi0x..." download="report.pdf">
Download report (PDF)
</a>
<!-- Browser compatibility notes:
- Chrome/Edge/Firefox: all work for files under ~2 MB
- Safari iOS: data URLs blocked for cross-origin PDFs; use blob: URLs instead
- PDFs over ~2 MB: switch to blob URLs — URL.createObjectURL(blob) -->DocuSign Payload Example
{
"emailSubject": "Please sign the attached contract",
"documents": [
{
"documentBase64": "JVBERi0xLjQKJcfs8vUKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZw...",
"name": "Contract.pdf",
"fileExtension": "pdf",
"documentId": "1"
}
],
"recipients": {
"signers": [{
"email": "[email protected]",
"name": "Alice Signer",
"recipientId": "1",
"tabs": {
"signHereTabs": [{
"documentId": "1",
"pageNumber": "1",
"xPosition": "100",
"yPosition": "150"
}]
}
}]
},
"status": "sent"
}Critical: documentBase64 must be the raw Base64 string without the data:application/pdf;base64, prefix and without line wrapping. DocuSign returns HTTP 400 INVALID_DOCUMENT_BASE64 if the string contains whitespace or the prefix.
Common Pitfalls Encoding PDFs
- Including the data URL prefix in API calls — Most APIs want raw Base64. Strip
data:application/pdf;base64,before sending. If your encoder returned a data URL, doresult.split(",")[1]. - MIME-wrapped Base64 in JSON — MIME Base64 has CRLF line breaks every 76 chars. JSON string values accept these, but most APIs reject them. Use single-line Base64 (
b64encodein Python,Base64.getEncoder()in Java) for API payloads; MIME-wrapped only for email bodies. - Encoding over 10 MB files synchronously — The browser FileReader API is async but still runs on the main thread. For large PDFs use a Web Worker so the UI stays responsive. In Node, use streams via
pipe+new Base64Encode()transform. - Not verifying magic bytes — A valid PDF starts with
%PDF-1.X(bytes25 50 44 46 2D 31 2E). After decode, checkdecoded.slice(0, 5) === b'%PDF-'to confirm you have a real PDF before writing to disk. - Expecting Base64 output to be stable across encoders — Line wrap length, padding presence, and alphabet choice all affect the output text. The bytes round-trip identically; the stringmay differ. Compare SHA-256 of the decoded bytes, not the Base64 strings.
Privacy — Why Browser-Only Matters
PDFs often contain confidential data: contracts, medical records, legal filings, invoices with financial detail. Upload-based converters send the file to a server before giving you Base64 — the server operator sees every byte, and may retain, log, or train on your document.
This tool uses only client-side JavaScript. The file is read viaFileReader, converted via btoa / data URL, and returned in the same page load. No network request carries the PDF. Verifiable by opening DevTools → Network: no POST or PUT is emitted when you encode.
Key Facts
- Size inflation:
- ~33% (4 chars per 3 input bytes)
- Data URL prefix:
- data:application/pdf;base64,
- For APIs:
- Raw Base64, no prefix, no line wrap
- For email:
- MIME variant — 76-char CRLF line wrap
- For HTML embed:
- Full data URL in iframe/object src
- PDF magic bytes:
- %PDF-1.X — verify after decode
- Browser limit:
- ~2 MB for data URLs; use blob URLs beyond
- Privacy:
- This tool is 100% client-side — no upload
Related Base64 Tools
- Base64 Encode File — language-agnostic encoder for any file type
- Base64 Encode Image — JPEG/PNG/WebP encoder
- MIME Base64 Encode — for email attachments (76-char wrap)
- Base64 Decode Online — decode back to a PDF file
- Base64 Encode in Python — PDF encoding in Python
- Base64 Encode in JavaScript — Node + browser
- Base64 Encode in Java — enterprise PDF pipelines