What Is Client-Side Encryption? Your Browser Does the Work
Client-side encryption means your files are encrypted in your browser before upload. Learn why this approach gives you maximum privacy and control.
Client-side encryption means your browser or app encrypts files on your device before anything touches the network. The server receives only ciphertext — AES-256-GCM output indistinguishable from random noise — and the decryption key never leaves the client. This is the opposite of server-side encryption, where the provider holds the keys and technically can read your files. The Web Crypto API (window.crypto.subtle) makes this possible in any modern browser without plugins, running at roughly 2–3 GB/s on AES-NI hardware. Services like HexaTransfer, SwissTransfer, Tresorit Send, and Proton Drive use this model to guarantee that files stay private even if the service itself is compromised.
The browser-as-cryptographic-engine shift
Five years ago, real encryption required installing a desktop app or using PGP on the command line. The Web Crypto API, standardized by W3C in 2017, changed this. It exposes AES-GCM, RSA-OAEP, ECDH, HMAC, PBKDF2, and SHA-256 directly to JavaScript running in any mainstream browser — Chrome, Firefox, Safari, Edge.
Performance is no longer the blocker. Intel AES-NI instructions hit 3–5 GB/s per core for AES-256-GCM. ARM's Cryptography Extensions on Apple M-series and Qualcomm Snapdragon chips deliver similar throughput. Encrypting a 1 GB file in the browser takes roughly 300–500 ms on a mid-range laptop.
The remaining challenge is handling files larger than browser memory. The Streams API and ReadableStream let code process files in 4 MB chunks, encrypting each chunk independently with a unique counter-mode IV. This is how services push the ceiling to 10 GB or more.
A minimal client-side encryption flow
Here's the sequence a typical browser-based service runs:
// 1. Generate a random 256-bit AES key
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]
);
// 2. Read the file in chunks
const file = fileInput.files[0];
const chunkSize = 4 * 1024 * 1024;
// 3. Encrypt each chunk with a unique 12-byte IV
for (let offset = 0; offset < file.size; offset += chunkSize) {
const chunk = file.slice(offset, offset + chunkSize);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv }, key, await chunk.arrayBuffer()
);
// 4. Upload [iv || ciphertext] to the server
}
// 5. Export the key and embed it in the share URL fragment
const keyBytes = await crypto.subtle.exportKey("raw", key);
const shareUrl = `https://example.com/d/${fileId}#k=${base64url(keyBytes)}`;
The server sees random-looking bytes, a file ID, and nothing else. The key exists only in the user's browser memory and the URL fragment.
Why this beats server-side encryption
Server-side encryption means the provider decrypts on request — to generate thumbnails, run virus scans, process search queries, or respond to legal demands. A 2023 Apple disclosure revealed that iCloud backups (which were not end-to-end encrypted until Advanced Data Protection launched) were accessible to Apple and therefore to US law enforcement under valid requests.
Client-side flips this. Because the key never reaches the provider:
- Rogue employees see nothing. The engineer with database access gets ciphertext.
- Subpoenas produce ciphertext. The provider can comply with warrants by handing over the encrypted blob, which is useless without the key.
- Breaches leak ciphertext. The 2021 LastPass incident showed this matters — stolen vaults were encrypted, and only users with weak master passwords faced real risk.
- Provider outages don't compromise data. Even if the company collapses, your local copy of the key (the URL) still decrypts the file.
What the server can still see
Client-side encryption protects file contents but not everything. The server typically observes:
- File size — the ciphertext length approximates plaintext length (AES-GCM adds 16 bytes of overhead per encryption, plus the 12-byte IV).
- Upload and download IP addresses with timestamps.
- Session metadata from TLS handshakes, including the client's TLS fingerprint.
- Encrypted filenames — unless filenames are included in the encrypted payload, they may leak.
Good client-side services encrypt filenames as part of the ciphertext header and padding to bucket sizes (1 MB, 10 MB, 100 MB) to obscure size. Tresorit and Proton Drive document their metadata exposure explicitly.
Password-protected client-side encryption
Many services let users add a password on top of the URL fragment. The flow:
- The browser generates a random 128-bit salt and derives a key using PBKDF2-HMAC-SHA-256 with 600,000 iterations (OWASP 2023 recommendation) or Argon2id with
memory=64 MB, iterations=3. - The file is encrypted with the derived key.
- The salt goes in the URL fragment; the password is communicated out-of-band.
- The recipient types the password, which re-derives the key locally.
This turns a single-channel share (URL is enough) into two-factor: the attacker needs both the link and the password. PBKDF2 with 600,000 iterations makes offline brute force cost roughly 10 seconds per guess on a modern GPU, so passwords need 40+ bits of entropy to resist determined attackers — think 10+ characters from a diverse alphabet.
The trust shift: from service to client code
Client-side encryption moves the trust boundary. Before, you trusted the service to handle your plaintext well. Now, you trust the JavaScript the service ships to your browser on every page load. A malicious update could exfiltrate the key before or during encryption.
Three mitigations exist, varying in rigor:
- Subresource Integrity (SRI) for script tags ensures the JS hash matches a known value.
- Code audits by firms like Cure53, NCC Group, or Trail of Bits verify the encryption logic is sound.
- Reproducible builds let independent parties confirm the shipped code matches published source.
- Content Security Policy (CSP) headers block third-party scripts that could tamper with encryption.
The strictest approach, used by Proton Mail's pmcrypto and some Electron-based clients, ships signed binaries rather than fresh JavaScript per visit. Browser-based services trade some of this rigor for zero-install convenience.
Use cases where client-side shines
A few scenarios where client-side encryption is worth the slightly slower first load:
- Legal and medical documents. HIPAA 45 CFR § 164.312 and attorney-client privilege both strongly benefit from provider-blind architectures.
- Journalism and source protection. Sending unredacted documents where even metadata exposure carries risk.
- Corporate IP. Board packs, financial models, M&A materials where insider threats at the file transfer provider are a realistic concern.
- Personal records. Tax documents, passports, medical tests — files you'd be uncomfortable seeing in a provider breach headline.
For low-sensitivity files (a meeting photo, a recipe), traditional server-side encryption is fine.
How to recognize real client-side encryption
Four signs a service actually does client-side encryption:
- URL fragments carry a key. The share URL has text after
#that looks like base64-encoded random bytes. - Uploads are ciphertext. Open DevTools → Network during upload; the request body should look like random bytes, not your filename.
- Large files still work fast. A true client-side flow streams chunks; it doesn't re-upload to a server-side encryption gateway.
- The privacy policy says "we can't decrypt your files." Paired with a technical whitepaper, not just marketing copy.
Services that pass: SwissTransfer's E2EE tier, Tresorit Send, Proton Drive shared links, Mega.nz, and HexaTransfer. Services that don't: WeTransfer (standard), Google Drive, Dropbox share links.
Putting it to work
If you want to try client-side encryption today, open DevTools and watch the Network tab while uploading a file. You should see an encrypted blob going to the server and a key in your URL bar that never appears in any request. That's the whole promise.
Try it at hexatransfer.com — free, no account, 10 GB max.
Send large files securely with end-to-end encryption
Transfer files up to 10 GB for free with end-to-end encryption. No account required. Your files are encrypted in your browser before upload — no one else can read them.
Send a file