The LocalStorage Hangover
Stop me if you've heard this one before: a developer builds a shiny new Next.js or Remix app, grabs a JWT from an auth provider, and tosses it straight into localStorage so it survives a page refresh. It’s the 'standard' way. It’s also a security disaster waiting to happen. For years, we’ve been sleepwalking into a pattern where our most sensitive session data is sitting in a wide-open treasure chest, accessible to any third-party script or browser extension that decides to get nosy.
The reality is that XSS (Cross-Site Scripting) isn't some relic of the 2000s. In fact, XSS was recently ranked as the #1 top threat of 2025 by MITRE and CISA. With over 7,300 documented vulnerabilities this year alone, relying on localStorage for session management is basically leaving your front door unlocked in a neighborhood that’s being actively patrolled by burglars. It’s time we shifted our perspective toward sealed cookies iron-web-crypto to reclaim the browser's security boundaries.
The Transparent Token Problem
Most developers use JSON Web Tokens (JWTs) because they are stateless. You don't need a database lookup to know who a user is; you just verify the signature. But here is the catch: standard JWTs are signed, not encrypted. Anyone with access to that token can paste it into a debugger and see your user's email, their internal ID, their subscription tier, and their permissions. It’s transparent. If your frontend can read it, so can an attacker.
By contrast, sealed cookies treat the session payload like a black box. Instead of just signing the data to prevent tampering, we use iron-web-crypto to fully encrypt it. This means the client—and any malicious script running on the client—cannot see what is inside the cookie. To the browser, it’s just an opaque string of gibberish. This is the 'defense in depth' strategy that modern web architecture demands.
Why iron-web-crypto is the Modern Choice
You might be familiar with @hapi/iron, the workhorse library that powered secure sessions in the Hapi and early Next.js ecosystems. While it was great, it was deeply tied to the Node.js crypto module. In the age of Edge Computing, Cloudflare Workers, and Vercel Edge Functions, that dependency is a dealbreaker.
The successor, iron-webcrypto, is a complete reimagining built on the native Web Crypto API. Because it uses native browser/runtime primitives instead of polyfilled JavaScript logic, it is significantly more efficient. Benchmarks show performance gains anywhere from 2x to 15x compared to legacy JS-based libraries. More importantly, it is ESM-only and runs perfectly in Bun, Deno, and the browser, making it the gold standard for web crypto api security.
AES-GCM: The New Standard
Older encryption implementations often relied on AES-256-CBC. While secure when implemented perfectly, CBC is notorious for being 'fussy'—get the padding wrong, and you’re vulnerable to padding oracle attacks. Modern libraries like iron-web-crypto have moved to AES-GCM (Authenticated Encryption with Associated Data). It’s faster, hardware-accelerated on most modern CPUs, and provides built-in integrity checking. It’s essentially 'footgun-proof' encryption for the busy developer.
Stateless Scaling at the Edge
The beauty of stateless session management is that it allows your application to scale without a bottleneck. If you use a traditional stateful session, every single request to your API requires a round-trip to Redis or Postgres just to see if the user is logged in. This adds latency and creates a single point of failure.
Sealed cookies give you the best of both worlds. Like a JWT, the server can decrypt the cookie, verify the user's identity, and check their roles entirely in memory. Because we are using sealed cookies iron-web-crypto, this happens at the 'Edge'—milliseconds away from the user—without ever touching a central database. According to Auth0’s research on stateless cookies, this architecture is the most effective way to reduce latency while maintaining high security standards.
The Trade-offs: Size and Invalidation
No architecture is perfect, and sealed cookies come with two primary constraints that you need to be aware of before you refactor your entire auth flow.
- The 4KB Ceiling: Browsers generally limit individual cookies to 4KB. Because encryption adds overhead (metadata, IVs, authentication tags), you can't just cram a 10MB user profile into a sealed cookie. If you try to store too much, the browser will silently drop the cookie, and your user will be logged out. Keep your session payloads lean—just IDs and essential roles.
- The Invalidation Problem: Since the session is stateless, you can't 'delete' it from the server side. If a user’s account is compromised, the sealed cookie remains valid until it expires. To solve this, you have to reintroduce a small amount of state, such as a 'denylist' of revoked IDs in a global cache like Upstash, which somewhat offsets the benefits of being fully stateless.
Moving Away from JWT LocalStorage Vulnerabilities
The transition is simple: Stop sending your session tokens to the frontend as JSON. Instead, set them as HttpOnly, Secure, and SameSite=Lax cookies. By doing this, you're effectively moving the token into a 'vault' that JavaScript cannot touch. Even if an attacker finds an XSS vulnerability on your page, they cannot use document.cookie to steal the session. When you combine this with the encryption provided by iron-web-crypto, you’ve effectively neutralized the most common jwt localstorage vulnerabilities.
The Verdict
The web is moving toward more secure, performant, and edge-ready primitives. Storing raw, readable JWTs in a place where any script can grab them is a legacy habit we need to break. By adopting sealed cookies iron-web-crypto, you are opting for a 'zero-trust' approach to your frontend. You get the speed of statelessness, the security of AES-GCM encryption, and the peace of mind that your user’s data isn't one XSS attack away from being leaked.
If you’re starting a new project in Next.js or Remix today, skip the LocalStorage hacks. Implement sealed cookies from the jump. Your users—and your security auditors—will thank you. Have you already made the switch to the Web Crypto API, or are you still wrestling with JWT bloat? Let’s talk about it in the comments.


