JWT Tokens Explained: What's Inside Your Auth Token?
JWT tokens are in every modern web application's Authorization header — but most developers just copy-paste the pattern without understanding what's inside. Here's a complete breakdown.
Ram

Every time you log into a modern web application, your browser stores a JWT token. Every subsequent API request sends it. But what exactly is inside that token? What makes it secure? When should you use JWT vs session cookies?
Decode any JWT instantly with our free JWT Decoder — no signing secret needed to inspect the header and payload.
What Is a JWT?
A JSON Web Token (JWT) is a compact, URL-safe token format for securely transmitting claims between parties as a JSON object. It is defined in RFC 7519.
A JWT is self-contained: it carries all the information needed to identify a user and their permissions within the token itself. The server doesn't need to look up a session database for every request — it just verifies the token's signature.
JWT Structure: Three Parts
Every JWT has three Base64URL-encoded parts separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlJhbSIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Format: HEADER.PAYLOAD.SIGNATURE
Part 1: Header
The header identifies the algorithm used to sign the token:
{
"alg": "HS256",
"typ": "JWT"
}
Common algorithms:
- HS256 (HMAC SHA-256): Symmetric signing — same secret used to sign and verify
- RS256 (RSA SHA-256): Asymmetric — private key signs, public key verifies
- ES256 (ECDSA): Elliptic curve — smaller keys, same security as RS256
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Part 2: Payload (Claims)
The payload contains the claims — statements about the user:
{
"sub": "user_12345",
"name": "Ram Charan",
"email": "ram@example.com",
"role": "admin",
"iat": 1746000000,
"exp": 1746003600
}
Registered Claims (standard):
| Claim | Meaning |
|---|---|
sub | Subject — typically user ID |
iat | Issued At — Unix timestamp of creation |
exp | Expiration — Unix timestamp after which token is invalid |
nbf | Not Before — token invalid before this timestamp |
iss | Issuer — who created the token |
aud | Audience — who the token is intended for |
jti | JWT ID — unique identifier for this token |
The payload is Base64URL-encoded (not encrypted). Anyone can decode it — including with our JWT Decoder. Never put sensitive data (passwords, private keys, SSNs) in a JWT payload.
Part 3: Signature
The signature is what makes JWTs secure:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
For HS256, the server takes the encoded header and payload, concatenates them with a dot, then runs HMAC-SHA256 with the server's secret key.
When the server receives a JWT:
- Separate header, payload, signature
- Re-compute signature using the same secret key
- Compare with the received signature
- If they match, the token is authentic and unmodified
Decoding vs Verifying: An Important Distinction
Decoding: Base64URL decode the header and payload. Anyone can do this — no secret needed. Our JWT Decoder does exactly this. Verifying: Check the signature using the secret/public key. Only the server (or anyone with the key) can verify authenticity.Decoding tells you what's in the token. Verifying confirms the token is genuine and unmodified.
JWT vs Session Cookies
| Factor | JWT | Session Cookie |
|---|---|---|
| Server storage | None (stateless) | Session store required |
| Scalability | Excellent (no DB lookup) | Requires session sharing across servers |
| Token invalidation | Difficult (wait for expiry) | Easy (delete session from store) |
| Payload size | Can be large (in header every request) | Small (just session ID) |
| Security | Secure if implemented correctly | Secure if HTTPS + httpOnly + SameSite |
| Best for | Microservices, APIs, mobile | Traditional server-rendered apps |
- Mobile apps where cookies are inconvenient
- Microservices where multiple services need to verify identity
- Cross-domain authentication (your API on
api.example.com, frontend onapp.example.com) - Stateless APIs at scale
When to Use Session Cookies
- Traditional server-rendered web apps (Rails, Django, Laravel)
- When you need immediate token revocation (logout invalidates immediately)
- When payload size matters (cookies are small, JWT payload can grow large)
Common JWT Security Mistakes
1. Storing JWT in localStorage
LocalStorage is accessible to any JavaScript on the page, making JWTs stored there vulnerable to XSS attacks. Store JWTs in httpOnly cookies to prevent JavaScript access.2. Not Validating the exp Claim
Many implementations decode the JWT and check the payload but forget to verify the token hasn't expired. Always validate exp server-side.
3. Using alg: none
Some JWT libraries allow alg: none which disables signature verification entirely. Explicitly reject tokens with alg: none in your server-side validation.
4. Weak Secrets for HS256
HS256 with a short or guessable secret is easily brute-forced. Use a randomly generated secret of at least 256 bits. For production, prefer RS256 or ES256 with proper key management.5. Long Expiry Times
Long-lived JWTs (days or weeks) are dangerous because they can't be invalidated if compromised. Use short expiry (15 minutes) for access tokens and longer expiry for refresh tokens, with a token refresh flow.Implementing JWT in Node.js
import jwt from 'jsonwebtoken';
const SECRET = process.env.JWT_SECRET;
// Generate token on login function generateToken(user) { return jwt.sign( { sub: user.id, email: user.email, role: user.role }, SECRET, { expiresIn: '15m' } ); }
// Verify token middleware function verifyToken(req, res, next) { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token' }); try { const payload = jwt.verify(token, SECRET); req.user = payload; next(); } catch (err) { return res.status(401).json({ error: 'Invalid token' }); } }
Debugging JWT Issues
When your API returns 401:
- Paste your token into our JWT Decoder
- Check
exp— is the token expired? Timestamps are in UTC - Check
issandaud— does your server validate these claims? - Check the
algin the header — does your server accept this algorithm? - Verify the Authorization header format:
Bearer <token>(note the space)
