A client sent me a contract last month. Before forwarding it to my accountant, I wanted to password-protect it. The usual options? Adobe Acrobat ($20/month) or some "free" online tool that uploads my file to a server first.
Neither worked for me. So I built client-side encryption into my PDF toolkit.
Here's how to do it entirely in the browser using pdf-lib.
Why Client-Side?
Most online PDF encryption works like this:
- Upload your PDF to their server
- They encrypt it
- You download the encrypted version
The problem: your sensitive document — contract, tax form, medical record — just sat on someone else's computer. Even if they delete it after processing, you have no guarantee.
Client-side encryption solves this. The PDF never leaves the user's device. The browser loads the encryption library, processes the file locally, and downloads the result.
The Stack: pdf-lib
pdf-lib supports PDF encryption natively as of v1.17.0. It runs entirely in the browser, no backend required.
npm install pdf-lib
Basic Encryption: Add a Password
Here's the simplest flow: load a PDF, encrypt it with a password, save.
import { PDFDocument } from 'pdf-lib';
async function encryptPdf(pdfFile, password) {
// Read PDF as bytes
const pdfBytes = await pdfFile.arrayBuffer();
// Load the PDF
const pdfDoc = await PDFDocument.load(pdfBytes);
// Encrypt with password
pdfDoc.encrypt({
userPassword: password,
ownerPassword: password,
});
// Save encrypted PDF
const encryptedBytes = await pdfDoc.save();
return encryptedBytes;
}
// Usage
const input = document.getElementById('fileInput');
input.addEventListener('change', async (e) => {
const file = e.target.files[0];
const encrypted = await encryptPdf(file, 'my-secret-password');
// Download
const blob = new Blob([encrypted], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'encrypted-document.pdf';
a.click();
});
What's happening:
- Read the PDF file into memory
- Load it into pdf-lib
- Apply encryption with the provided password
- Save and trigger download
The resulting PDF requires the password to open.
User Password vs Owner Password
PDF encryption supports two passwords:
- User password: Required to open and view the document
- Owner password: Required to change permissions (printing, copying, etc.)
For most use cases, you can set them to the same value:
pdfDoc.encrypt({
userPassword: 'open-me',
ownerPassword: 'open-me',
});
If you want stricter control, set different passwords:
pdfDoc.encrypt({
userPassword: 'shared-with-client', // Client needs this to open
ownerPassword: 'admin-only-password', // Only you can change permissions
});
Encryption Strength: 128-bit vs 256-bit AES
PDF supports multiple encryption algorithms:
| Algorithm | Security | Compatibility |
|---|---|---|
| RC4 (40-bit) | Weak | Universal |
| 128-bit AES | Strong | Acrobat 7+ |
| 256-bit AES | Strongest | Acrobat X+ |
pdf-lib defaults to strong encryption. For maximum compatibility with older readers, you can explicitly request 128-bit AES. For best security, use 256-bit AES.
Most modern PDF readers (Adobe Acrobat, Preview, Chrome/Firefox built-in viewers, mobile apps) handle 128-bit and 256-bit AES without issues. Only very old software (pre-2010) might struggle.
Setting Permissions (Optional)
Beyond just password-protection, you can restrict what users can do:
pdfDoc.encrypt({
userPassword: 'view-only',
ownerPassword: 'admin-password',
permissions: {
printing: 'highResolution', // or 'lowResolution' or false
modifying: false,
copying: false,
annotating: false,
fillingForms: false,
documentAssembly: false,
},
});
This creates a PDF that opens with "view-only" but restricts printing, copying, and editing unless you have the owner password.
Password Best Practices
Since there's no "forgot password" feature for encrypted PDFs:
-
Use a memorable but strong password. I use 3-4 random words + a number + a symbol:
Blue-Desk-7-Tiger! - Share the password separately. Email the PDF, text the password. Don't send both through the same channel.
- Test before sending. Always open the encrypted PDF yourself to verify the password works.
Complete Working Example
Here's a minimal HTML page that encrypts PDFs in the browser:
<!DOCTYPE html>
<html>
<head>
<title>PDF Encryptor</title>
<script type="module">
import { PDFDocument } from 'https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/+esm';
document.getElementById('encryptBtn').addEventListener('click', async () => {
const fileInput = document.getElementById('pdfFile');
const password = document.getElementById('password').value;
if (!fileInput.files[0] || !password) return;
const pdfBytes = await fileInput.files[0].arrayBuffer();
const pdfDoc = await PDFDocument.load(pdfBytes);
pdfDoc.encrypt({
userPassword: password,
ownerPassword: password,
});
const encrypted = await pdfDoc.save();
const blob = new Blob([encrypted], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'encrypted.pdf';
a.click();
});
</script>
</head>
<body>
<input type="file" id="pdfFile" accept=".pdf">
<input type="password" id="password" placeholder="Enter password">
<button id="encryptBtn">Encrypt PDF</button>
</body>
</html>
No build step, no server, no upload.
Try the Live Tool
If you want to encrypt PDFs without writing code:
Free, no signup, browser-based. Your file never leaves your device.
The source code is open source if you want to see the full Vue 3 + pdf-lib integration with 128-bit and 256-bit AES options.
Have you implemented client-side document security? What trade-offs do you consider between security and usability?
Top comments (0)