Demystifying Cookies, Sessions, and JWTs
Let’s be honest — authentication and state management are one of those things every developer uses but only a few really understand. And yet, these are the foundations of secure, scalable web apps. So today, let’s clear the fog around cookies, sessions, and JWTs (JSON Web Tokens). We’ll break down what they are, why they exist, how they differ, and most importantly, when to use which.
First things first: Why do we even need these?
HTTP, the protocol powering the web, is stateless. That means every request your browser makes is treated as if it’s the first time it has ever spoken to the server. Imagine walking into a restaurant and the waiter forgets your order every time you open your mouth. That’s HTTP.
So to make web apps feel alive (personalized dashboards, logged-in states, shopping carts, etc.), we need ways to persist identity and state across multiple requests. That’s where cookies, sessions, and JWTs come in.
1. Cookies
Think of cookies as tiny storage boxes that your browser keeps and attaches to every request made to a specific domain.
- How they work:
- Server sends a
Set-Cookie
header → browser stores it. - On the next request, the browser automatically sends it back via the
Cookie
header.
- Server sends a
- Example:
Set-Cookie: userId=12345; HttpOnly; Secure
Now, every time you hit the server, your browser says:
Cookie: userId=12345
- Pros:
- Simple and widely supported.
- Good for lightweight data (preferences, tokens).
- Cons:
- Limited storage (usually ~4KB).
- Vulnerable to XSS if not marked
HttpOnly
. - Automatically sent on every request → can bloat traffic.
Real-world analogy: Cookies are like those little claim tickets you get at a coat check. Small, easy to carry, but they don’t hold your coat themselves — they just reference it.
2. Sessions
Cookies are useful, but you don’t want to cram sensitive or bulky info into them. That’s why sessions exist.
- How they work:
- When you log in, the server creates a session (say in memory, Redis, or a DB) and stores info like
userId
,roles
,cartId
. - The server gives you a session ID (usually stored in a cookie).
- On each request, your browser presents the session ID, and the server looks it up to find your data.
- When you log in, the server creates a session (say in memory, Redis, or a DB) and stores info like
- Example:
- Server stores:
sessionId=xyz123 → {userId: 42, cart: [items...]}
. - Browser sends:
Cookie: sessionId=xyz123
. - Server retrieves the full session from storage.
- Server stores:
- Pros:
- Scales well for user state (you can store as much as your DB/cache allows).
- Keeps sensitive info server-side, not in the browser.
- Easy to invalidate (kill the session → user logged out everywhere).
- Cons:
- Requires session storage infrastructure.
- Not great for distributed systems unless you use centralized storage like Redis.
- Can struggle at massive scale if not managed well.
Real-world analogy: A session is like leaving your coat at the coat check. You only keep a tiny ticket (session ID), but the heavy stuff is safely stored inside.
3. JWT (JSON Web Token)
JWTs are the new kid on the block, popular with modern APIs. Unlike sessions, JWTs are stateless — the server doesn’t need to remember who you are because all the info is inside the token itself.
- Structure:
header.payload.signature
Encoded in Base64. Example:
{
"sub": "12345",
"role": "admin",
"exp": 1713992331
}
- How they work:
- You log in → server creates a JWT signed with a secret key.
- Client stores the token (usually in localStorage or an HttpOnly cookie).
- On each request, client attaches
Authorization: Bearer <token>
. - Server verifies the signature → if valid, trusts the claims inside.
- Pros:
- Stateless → no need for centralized session storage.
- Scales beautifully in microservices/cloud.
- Self-contained (role, expiry, metadata included).
- Cons:
- Harder to revoke (once a token is out there, it’s valid until it expires).
- Bigger than a simple session ID → increases request size.
- If stored improperly (e.g., localStorage), vulnerable to XSS.
Real-world analogy: JWTs are like a passport. It has all your info inside and is signed by an authority. Any border checkpoint (service) can trust it without calling the passport office.
When to Use What?
- Cookies only: For storing small, non-sensitive preferences (theme, language).
- Sessions: Best for classic web apps where you control both frontend and backend. Example: e-commerce shopping carts, user dashboards.
- JWTs: Best for APIs, microservices, or mobile apps where scaling matters. Example: A distributed system with multiple services that all need to verify identity without hitting a central database.
Practical Tip: Hybrid Approach
In the real world, teams often mix these:
- Store JWT in a cookie (for browser apps).
- Use sessions for short-lived, highly dynamic state (shopping cart).
- Add refresh tokens with JWTs for better security.
Wrapping Up
Cookies, sessions, and JWTs aren’t competitors — they’re tools. Understanding how they work helps you pick the right one for the right job instead of blindly following trends.
Next time you log in somewhere, pause for a second and think: “Am I holding a ticket, a coat, or a passport?” 😉
Member discussion