TLDR: Client-side AES-256-GCM keeps honest people honest. It is NOT a substitute for real access control at the data layer. Know exactly what you bought.
The Setup
We built a full workshop site for a marketing client's AI event — slides, an embedded website, the works.
After the event, I needed to lock it down fast.
Attendees had the link. The content was proprietary. I didn't want to spin up auth infrastructure for a static site.
So we shipped something genuinely clever.
What We Built
lockdown.js — a build-time script that walks every file under site/, encrypts it with AES-256-GCM (AES-256-GCM: a symmetric authenticated encryption standard — same thing your bank uses), derives the key via PBKDF2-SHA256 (a key-stretching function that makes brute-force painful) at 250,000 iterations from a shared password, and writes the ciphertext out as <path>.enc.
The plaintext gets deleted.
Every HTML path becomes a universal unlocker page.
A service worker at site/sw.js intercepts every same-origin fetch, pulls the .enc blob, decrypts it with the key stored in IndexedDB (the browser's local key-value store), and streams the plaintext back with the right MIME type.
From the outside: just a password prompt.
From the inside: the full site, exactly as built.
Honestly kind of beautiful.
What It Does
It filters the crowd.
Any scraper, casual visitor, or Googlebot hits the wall.
The 250,000 PBKDF2 iterations mean brute-forcing the password is slow enough to matter — not cryptographically impossible, but a real deterrent for a shared-audience use case.
For a closed workshop with a known attendee list? EXACTLY the right tool.
What It Doesn't Do
Here's where I want to be honest with you.
Right around that same stretch of work, a developer I work with flagged a completely different disaster: a Supabase project where the row-level security (RLS) policies — the per-table rules that control who can read or write what — were set to using (true).
Meaning: any authenticated Google account could hit PostgREST (Supabase's auto-generated REST API layer, which talks directly to the database) and pull whatever they wanted.
No app-layer check stopped them. No encryption was in play. The floor was just… open.
We're talking a read leak of 56,193 Shopify orders plus refunds and recharge data, reachable by any Google account that could authenticate.
The app looked locked. Google OAuth at the front door, session checks everywhere in the code.
Didn't matter. PostgREST bypasses all of that. The data layer had no teeth.
And no amount of clever browser-side encryption could have saved it.
The Line That Matters
Client-side crypto secures the delivery.
Server-side access control secures the data.
You need both. You cannot swap one for the other.
I've watched the same mistake surface in HIPAA conversations too — "we encrypt everything client-side, we literally can't read it" — and it still doesn't get you off the hook.
HHS is clear on this: encryption lowers the blast radius of a breach. It does not shrink your legal surface area. You still sign BAAs. You still close your RLS policies. You still run audit logging.
The crypto buys you margin. It doesn't buy you exemptions.
Why This Matters to Me
The marketing client lockdown was a precise, elegant tool deployed at the right layer for the right threat.
The Supabase situation was the reminder that brilliant crypto at the edge means nothing if the floor underneath is wide open.
Build the gate. And lock the vault.