JWTs vs session cookies for authentication
Neither is universally “more secure” — they make different trade-offs, and mixing up what each one actually guarantees is where most confusion starts.
The core trade-off: statelessness
A traditional session cookie holds an opaque identifier; the server looks up the actual session data (who the user is, what they’re allowed to do) in its own store on every request. That lookup is the cost, but it also means the server can revoke a session instantly by deleting it server-side. A JWT carries its claims directly in the token itself, signed so the server can verify they haven’t been tampered with — no lookup needed, which is the appeal at scale. The cost shows up at revocation: since the server isn’t tracking issued tokens, invalidating one before its natural expiry means either keeping a revocation/deny list (which reintroduces the server-side state a JWT was meant to avoid) or just waiting it out with a short expiry.
A JWT's payload is readable, not secret
The three segments of a JWT — header, payload, signature — are base64url-encoded, not encrypted. Anyone holding the token, including the end user themselves, can decode the payload and read every claim in it. The signature proves the claims haven’t been altered since the server issued them; it says nothing about who can read them. Putting a secret value into a JWT’s payload on the assumption that it’s protected is the most common mistake here — if a claim shouldn’t be visible to whoever holds the token, it doesn’t belong in the payload at all.
Decoding is not verifying
Because a JWT’s payload is just base64url, decoding one tells you what it claims, not whether those claims are genuine — that requires checking the signature against the issuer’s actual key, which a decoding tool that only reads the payload does not do. Treat any claims from an unverified token as untrusted input.