Skip to content
Engineering

Sessions vs JWTs, Picking Web Auth Storage

BYOB Team

BYOB Team

8 min read

Use server sessions for traditional web apps where immediate revocation and simple control matter. Use short lived JWTs for stateless APIs, edge runtimes, and mobile clients where per request lookups hurt. Most solid setups mix both, with refresh token rotation, strict claims checks, and row level security underneath.

Key takeaways

  • • Server sessions favor immediate revocation and simple control, and logout means deleting one row
  • • JWTs favor stateless APIs, edge runtimes, and mobile clients, and every verifier must check signature, issuer, audience, and expiry
  • • Keep JWT claims small and short lived, and never put secrets or fast changing permissions inside
  • • Combine refresh rotation with row level security so app bugs do not become data leaks
Sessions vs JWTs, Picking Web Auth Storage

Think of sessions as a coat check. You hand over the coat, you get a small ticket, the attendant keeps the real thing behind the counter. JWTs are the opposite. They are a stamped passport you carry yourself, and every border guard reads the stamps instead of calling your home office.

Both get you through the door. The difference is who holds the truth, and how fast you can cancel it.

Short answer, revocation need decides #

Use server sessions for a traditional web app rendered by your server. The server stores session state, the browser holds an opaque ID, and logout or compromise means deleting one row. Revocation is immediate and easy to reason about.

Use JWTs when clients call stateless APIs across services, edge runtimes, or mobile apps where a session lookup on every request is costly or impractical. The token carries signed claims the API can verify without a database round trip.

Decision rule: need instant revoke and simple server control, pick sessions. Need stateless verification across boundaries, pick short lived JWTs plus refresh token rotation. Most mature apps end hybrid.

flowchart TD A[Start auth design] --> B{Web app or API client} B -->|Web app| C[Use server session] B -->|API client| D[Use short lived JWT] C --> E[Store session row and set cookie] D --> F[Issue token plus rotate refresh]

How do sessions and JWTs work? #

Session auth keeps authority on the server. The cookie presents an ID. The server checks users, sessions, and accounts tables, then decides. The browser token is meaningless by itself, which is the point. Steal the ticket and you only hold a pointer. Cancel the pointer and the theft ends.

JWT auth moves a bounded set of assertions to the client: header, payload claims, and signature. As stated in RFC 7519 (https://datatracker.ietf.org/doc/html/rfc7519), a JWT is a compact claims representation meant to be transferred between parties, and the claims carry the assertions. The API validates signature, issuer, audience, and expiry, then trusts the claims inside for routing and coarse authorization. Fine grained data access still belongs in the database.

This is where teams go wrong. They treat a valid signature as proof of permission. It is not. It is proof of issuance. Permission needs a fresh check against current state, either in app logic or in the database itself.

Better Auth style apps typically favor session rows for web flows, which fits D1 backed users and sessions tables well. JWTs enter when APIs, edge functions, or third party clients need verifiable tokens without session affinity.

How do sessions and JWTs compare? #

Criterion Server sessions JWTs
Revocation Immediate, delete session Needs denylist, version, or short lifetime
Scaling reads Session lookup per request Verify signature, no lookup
Edge fit Requires session store access Strong fit for edge verification
Claim freshness Always current from DB Stale until token expires
Token size Tiny cookie ID Larger, grows with claims
Best fit Server rendered web apps APIs, mobile, cross service calls

Neither eliminates authorization logic. Both still need server side checks before returning data.

Which rules keep auth safe? #

Keep JWT claims small #

Include subject, issuer, audience, issued at, expiry, and only stable scopes needed for routing. Validate all of them on every request. As stated in RFC 8725 on JWT best practices (https://datatracker.ietf.org/doc/html/rfc8725), implementations must validate the signature with the expected algorithm and reject tokens that violate assumptions. Large or sensitive claims leak through logs, caches, and browser storage. Never put secrets, passwords, or fast changing permissions inside.

js
// Verify everything, trust nothing implicit
const payload = await verifyJwt(token, {
  issuer: 'https://auth.yourapp.com',
  audience: 'api.yourapp.com',
  clockTolerance: 30
});
if (payload.exp <= Date.now() / 1000) throw new Error('expired');

Use short lifetimes plus refresh token rotation #

Issue short lived access tokens and longer refresh tokens that rotate on each use. Reuse of an old refresh token signals possible theft and should revoke the chain. Store refresh state server side even in JWT architectures. As stated in the OWASP JWT cheat sheet (https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html), JWTs suggested for stateless sessions need a managed invalidation answer such as a denylist or short expiration, or the stateless benefit collapses.

Plan revocation explicitly #

Sessions revoke by deletion. JWTs need expiry plus a revocation path: token version per user, denylist for compromised tokens, or forced reauthentication on sensitive actions. As stated in the OWASP session management cheat sheet (https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html), session IDs must be regenerated at privilege changes and invalidated at logout, and the same termination discipline applies to token chains. Stateless never means unrevocable.

Secure transport and storage #

Use HttpOnly, Secure, SameSite cookies for web sessions and web JWTs where possible. Avoid localStorage for long lived credentials, since any script in the origin can read them. Scope cookies to the correct domain and enforce HTTPS in production, including preview safe host handling. The OWASP ASVS session requirements (https://asvs.dev/v4.0.3/V3-Session-management/) treat unique, unpredictable, invalidatable sessions as baseline, not bonus.

js
// Cookie shape for web session IDs and web JWTs
cookies.set('session', sessionId, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  path: '/'
});

Enforce row level security #

Application checks can drift. Where the platform supports it, add row level security policies so the database independently restricts rows to their owner. Sessions and JWT derived identities both benefit from this second layer. A confused handler can still only return rows the policy allows.

Try it: JWT Decoder

Try it right here: jwt decoderOpen full tool

Loading the interactive tool… or open it here.

When should you use sessions and when should you use JWTs? #

Default to sessions for server rendered web apps where immediate logout, account suspension, and simple audits matter. The operational model is boring in the best way.

Choose JWTs when architecture demands stateless verification: edge APIs, mobile clients, or service to service calls. Keep them short lived, validate claims strictly, rotate refresh tokens, and retain server side revocation records.

Pick from revocation and topology needs, not from which pattern sounds more modern. The coat check and the passport both work. Just know which one you handed your users before someone loses theirs.

What are the trade-offs? #

Revocation need decides: sessions for server rendered apps, JWTs for stateless edge, mobile, and service calls.

Where sessions win Where JWTs win
Instant logout and suspension by deleting one row Signature checks with no lookup across edge and APIs
Simple audits tied to server tables Mobile and service clients that cannot hit a session store per call
Opaque browser IDs with HttpOnly Secure SameSite cookies Short lifetimes plus rotation for scoped access

Pick sessions when logout must bite immediately and row rules guard data. Pick JWTs when verification must happen without a store lookup.


Who this is for (and who should skip it) #

This comparison helps web builders choosing between revocable server sessions and stateless JWTs. If you need logout and suspension that take effect right away, the revocation test decides.

Skip JWTs for primary web sessions when you need instant revocation or row level rules tied to session rows. Use short lived tokens for APIs and keep web auth in sessions that you can delete in one place.

One limit to know. JWTs stay valid until expiry, so short lifetimes plus rotation plus a denylist or version check are required. Sessions revoke in one row delete but need store access on every request, which strains edge use.

  • Best for developers picking server sessions for web apps and short-lived JWTs for APIs.
  • Best for startups needing instant revocation on logout or compromise.
  • Best for freelancers wiring auth storage with rotation and row-level checks for clients.

What we learned building this #

BYOB uses server sessions for web auth and short lived tokens for APIs, with session rows in D1 and handle in HttpOnly Secure SameSite cookies, as laid out in the BYOB authentication guide. The session layer exposes login checks and redirects so server routes can verify the session and apply row level rules. That matches the post guidance of sessions for web and JWTs for APIs done carefully, and live docs at https://byob.studio/docs/authentication return 200 describing Better Auth plus D1.

How we picked these

We compared server sessions and JWTs using revocation, rotation, and row level security needs plus BYOB session wiring in D1. We kept only options with clear logout and suspension behavior and we favored web sessions that revoke in one place.

Frequently asked questions

Are sessions safer than JWTs?

Neither is universally safer. Sessions simplify immediate revocation and server side control. JWTs scale without session lookups but need strict claims validation, short lifetimes, rotation, and an explicit revocation plan, as stated in the OWASP JWT cheat sheet (https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html).

What belongs in JWT claims?

Small stable identifiers such as subject, issuer, audience, expiry, and a role or scope needed for routing. As stated in RFC 7519 (https://datatracker.ietf.org/doc/html/rfc7519), registered claims are interoperable plumbing, not a place for PII or secrets.

How do I revoke a JWT?

You cannot unsign it, so you expire it fast and keep a server side lever: a denylist, a per user token version, or rotation that detects reuse. The OWASP session guidance (https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html) treats termination as a first class control for both models.

Changelog

  • • Added fit guide, comparison table, and hands on notes
  • • Sep 2026 content upgrade, trade-offs section added and statement H2s converted to question form where meaning stayed the same

About the Author

BYOB Team

BYOB Team

The creative minds behind BYOB. We're a diverse team of engineers, designers, and AI specialists dedicated to making web development accessible to everyone.

Ready to start building?

Join thousands of developers using BYOB to ship faster with AI-powered development.

Get Started Free