v9.0.0-beta.0
Pre-releaseTry it:
pnpm add iron-session@betalatest still installs v8, nothing changes unless you opt in. Report problems in #951.
Upgrading is usually two changes: Node 22+, and Date.now() instead of new Date() in session data. Full guide: MIGRATION.md.
What's new
- Sessions work in Next.js Proxy (middleware)
- Sessions can be bigger than 4KB
getIronSession(await cookies(), options)typechecks, delete youras any- Four bugs that lost sessions or logouts without an error are fixed
onUnsealErrortells you why a cookie was rejected- TypeScript catches reads of a session that may not exist
- Node 22.13+, ESM-only
- Tested in real browsers now: Chromium, Firefox, WebKit, plus Node, Bun and Deno
1. Sessions work in Next.js Proxy (middleware)
New nextProxyCookies adapter:
// proxy.ts (middleware.ts before Next 16)
import { NextResponse, type NextRequest } from "next/server";
import { getIronSession, nextProxyCookies } from "iron-session";
export async function proxy(request: NextRequest) {
const response = NextResponse.next();
const session = await getIronSession(nextProxyCookies(request, response), options);
session.lastSeen = Date.now();
await session.save();
return response;
}The save reaches the browser, and code running later in the same request sees the new value, so refreshing or rotating a session in Proxy (middleware) finally works.
2. Sessions can be bigger than 4KB
Browsers cap a cookie at 4096 bytes. chunk: true splits the session across up to 4 cookies and puts it back together when reading. Turning it on or off does not sign anyone out.
Keep it as an escape hatch: cookies travel on every request and proxies cap the whole header around 8KB. For lots of data, store an id in the session and the data in your database.
Thanks @sefasenturk95 for pushing this (#937).
3. cookies() typechecks
const session = await getIronSession<Session>(await cookies(), options);- This failed to compile before, and the common workaround was
as anyon a session that guards your app. - Our
CookieStoretype now matches what Next actually returns. - Our CI typechecks against the real Next.js types, so a Next update breaks our build, not yours.
Fixes #840.
4. Four silent session bugs fixed
Each of these lost a cookie or kept a user signed in, with no error anywhere:
save()afterdestroy()restored the session you just destroyed, so a logout could be cancelled by any code that saves at the end of a request. Writing to a destroyed session and saving it throws now.updateConfig({ password })ignored the new password, so a rotation did nothing. It rotates now.- A
ttlunder 61 seconds produced a cookie the browser refuses. It keeps its full length now. - A
cookieOptions.expiresdate created at module scope goes stale, and the browser then drops every cookie you set. Rejected now, usettlinstead. Very likely the cause of #910.
5. See why a cookie was rejected
An unreadable cookie starts a fresh session instead of crashing the request. That stays, but it used to hide real problems. Now you can log them:
onUnsealError: (reason, error) => {
if (reason !== "expired") logger.warn({ reason }, "session cookie rejected");
};"unknown-password" means a password rotation went wrong. "invalid" can mean someone is tampering with cookies.
6. TypeScript catches missing sessions
- const id = session.user.id;
+ const id = session.user?.id;On a first visit the session is empty, but the old types said user was always there, so this compiled and crashed at runtime. Now TypeScript points at it before you ship.
Fixes #661, thanks @mattrossman (#842).
7. Node 22.13+, ESM-only
Node 20 is end of life. require("iron-session") keeps working, Node 22+ can require ES modules.
8. Also
- Store
Date.now()instead ofnew Date(). v8 silently turned your Date into a string, v9 throws and the error message says what to store instead. - Cookies from v6 (pre-2023) are no longer read, those users sign in once more. v8 and v9 cookies work in both directions, so a deploy rolls back safely.
- The "Safari doesn't save my cookie" mystery (#870): every browser refuses a
secure: truecookie over plain http, not just Safari. Usesecure: process.env.NODE_ENV === "production"in development. - 2 runtime dependencies instead of 3. Sealing a session takes about a tenth of a millisecond.
- New SECURITY.md. Test coverage went from 86% to 97%, and the suite now runs on Node 22/24/26, Bun, Deno, and in Chromium, Firefox and WebKit.
Superseded by v9.0.0-beta.1, which softened four guards that broke working code. The two bullets above describe the beta.1 behaviour.