How to Encrypt a File Before Uploading: AES-256 in the Browser
Encrypt files client-side with AES-256-GCM using the Web Crypto API before uploading to cloud storage. Zero-knowledge upload tutorial with working code.
When handling sensitive personal documents, medical records, or proprietary source code, zero-knowledge client-side encryption ensures that files are encrypted in the user's browser before transmission. Even if cloud storage is breached, the stored data remains encrypted.
Cryptographic Setup: AES-256-GCM
We use AES-GCM (Galois/Counter Mode) via the browser's native Web Crypto API:
- Provides 256-bit symmetric encryption.
- Built-in authenticated data (integrity check) prevents tampering.
- Requires a unique Initialization Vector (IV) for every encryption pass.
Download a standard text test file to test your browser-based AES-256 encryption and decryption functions.
Download 50KB TXT Sample →Complete Client-Side Encryption Implementation
export interface EncryptedPayload {
cipherBuffer: ArrayBuffer;
iv: Uint8Array;
}
// 1. Generate an AES-256 Encryption Key
export async function generateAesKey(): Promise<CryptoKey> {
return await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true, // extractable
['encrypt', 'decrypt']
);
}
// 2. Encrypt File ArrayBuffer
export async function encryptFile(file: File, key: CryptoKey): Promise<EncryptedPayload> {
const iv = crypto.getRandomValues(new Uint8Array(12)); // 96-bit IV recommended for AES-GCM
const plaintextBuffer = await file.arrayBuffer();
const cipherBuffer = await crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: iv,
},
key,
plaintextBuffer
);
return { cipherBuffer, iv };
}
// 3. Decrypt CipherBuffer
export async function decryptFile(
cipherBuffer: ArrayBuffer,
key: CryptoKey,
iv: Uint8Array
): Promise<ArrayBuffer> {
return await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: iv,
},
key,
cipherBuffer
);
}
Key Management Considerations
- Password derivation: Never use raw user passwords directly as keys. Derive the AES key using PBKDF2 with at least 600,000 iterations and a unique salt.
- Store the IV: The 12-byte IV is not a secret. Prepend the IV to the encrypted ciphertext before transmitting to the server.
Nguyen Dai Long
AuthorBackend Lead • Distributed Systems & Cloud Edge Architecture Specialist
4+ years designing high-throughput file ingestion pipelines, database architectures, and distributed edge storage on Cloudflare R2 & AWS S3. Founder of FileDummy and the NDL Ecosystem.
Was this article helpful?
Click Like to support the author and help other developers discover this guide.
Engineering Discussion & Feedback (0)
Share benchmark results, report edge cases, or ask technical questions.
No comments yet. Be the first developer to start the discussion!
Frequently Asked Questions
Why AES-256-GCM instead of AES-256-CBC?â–¾
AES-256-GCM provides both encryption and authenticated integrity verification in one operation — any tampering with the ciphertext is detected on decryption. AES-256-CBC only encrypts. GCM is the modern standard and is natively supported by the Web Crypto API.
Is client-side encryption safe from the server?â–¾
Client-side encryption with a user-held key achieves zero-knowledge — the server never sees plaintext. However, if the encryption code is served by the server, a compromised server could serve malicious JavaScript. True zero-knowledge requires code verification via subresource integrity or a native app.
What happens if I lose the encryption key?â–¾
The file is permanently unrecoverable. AES-256-GCM is computationally infeasible to break without the key. Always back up the key securely, derived from a password with PBKDF2 and salt.