JWTs Explained: How to Decode and Understand JSON Web Tokens
If you've built or consumed a REST API in the last decade, you've almost certainly encountered a string that looks like eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c. That's a JSON Web Token — a JWT — and despite looking like encrypted gibberish, most of it is just Base64-encoded JSON that anyone can read. Understanding the structure, what the parts mean, and crucially what JWTs do not protect will make you a safer and more effective developer.
The Three Parts of a JWT
A JWT is always three Base64url-encoded strings joined by dots: header.payload.signature.
Header — A small JSON object describing the token type and the signing algorithm. A typical header is {"alg":"HS256","typ":"JWT"}. The algorithm field (alg) matters a lot for security — more on that shortly.
Payload — The claims: the actual data the token carries. Standard registered claims include sub (subject — typically a user ID), iat (issued at), exp (expiration time as a Unix timestamp), and iss (issuer). You can add any custom claims alongside them.
Signature — A cryptographic value computed by hashing the header and payload together with a secret key (for HMAC algorithms like HS256) or signing with a private key (for RSA/ECDSA algorithms like RS256). This is what prevents tampering.
JWT Is Encoded, Not Encrypted
This is the most important thing to understand about JWTs: the header and payload are not secret. Base64url encoding is reversible by anyone without a key. If you paste the first two parts of any JWT into a Base64 decoder (or our JWT Decoder), you can read the claims immediately.
What the signature does protect is integrity. A server that verifies the signature knows that the payload hasn't been modified since the token was issued. It does not know whether the payload is confidential, because it isn't. Never put passwords, credit card numbers, or other sensitive secrets in a JWT payload.
How Signature Verification Works
With a symmetric algorithm like HS256, the server signs the token with a secret key and later verifies it with the same key. This is simple but requires every service that validates tokens to know the secret — a risk in microservice architectures where many services need to validate tokens.
With an asymmetric algorithm like RS256, the auth server signs with a private key and publishes a corresponding public key (often via a JWKS endpoint). Any service can verify tokens using the public key without ever seeing the private key. This is the preferred approach for distributed systems and is used by most OAuth 2.0 identity providers.
Common Use Cases
Authentication — After a user logs in, the server issues a JWT. The client stores it (usually in memory or an HttpOnly cookie) and sends it in the Authorization: Bearer header on subsequent requests. The server validates the signature instead of querying a session database.
Authorization — Custom claims in the payload can carry role or permission information: {"role":"admin","permissions":["read","write"]}. Services can make access decisions without a database lookup.
Stateless API access — Because the server needs no session store, JWTs are popular for stateless microservices and mobile apps where server-side session management is expensive.
Security Pitfalls to Avoid
The “alg: none” attack. Some early JWT libraries accepted tokens with "alg":"none" in the header — meaning no signature at all. Always explicitly specify and validate the expected algorithm on the server. Never trust the algorithm declared in the header without checking it against an allowlist.
Storing JWTs in localStorage. Tokens in localStorage are accessible to any JavaScript running on the page, making them vulnerable to XSS attacks. Prefer HttpOnly cookies, which JavaScript cannot read.
Long-lived tokens without refresh. A JWT that expires in 30 days is a long window for a stolen token to be abused. Use short exp values (15–60 minutes) with a separate refresh token flow.
Putting secrets in the payload. The payload is readable by anyone with the token. Include only what downstream services need to make decisions — typically a user ID, roles, and an expiration time.
Decode a JWT Instantly in Your Browser
When you receive an unfamiliar token — from an auth provider, an API response, or a debug log — the fastest way to inspect it is our JWT Decoder. Paste the token and instantly see the decoded header and payload, the signing algorithm, and the expiration time in human-readable format — entirely client-side, so the token never leaves your browser. Pair it with the Base64 Encoder/Decoder if you want to manually inspect or construct any of the three parts.