What a JWT decoder shows you
A JWT decoder takes the long eyJ... string from an Authorization header or cookie and turns it back into the JSON it was built from. A JSON Web Token has three parts separated by dots: a header that names the signing algorithm, a payload of claims about the user or client, and a signature. The first two are just base64url-encoded JSON, so anyone can read them. The signature is what proves the token was issued by someone holding the key and has not been edited since.
header {"alg":"HS256","typ":"JWT"}
payload {"sub":"user_8f3a21","role":"editor","exp":1893456000}
signature HMACSHA256(base64url(header) + "." + base64url(payload), secret)
How to decode and verify a JWT
- Paste the token into the box. A leading
Bearerand stray whitespace are removed for you. The colored view shows which part is the header, payload and signature. - Read the badges. They show the algorithm, whether the token is expired, not yet valid or currently valid, and how long until that changes.
- Check the claims table. Registered claims such as
exp,iatandnbfare converted from Unix seconds into your local time with a relative date. If you need to inspect a date by hand, the Unix timestamp converter handles any time zone. - Verify the signature. For HS256, HS384 and HS512 enter the shared secret. For RS256, PS256 and ES256 (and their 384 and 512 variants) paste the public key as a PEM block or a JWK. The result says plainly whether the signature is valid.
- Build a test token in the encoder if you need one for local development: edit the JSON, set a secret and copy the signed token.
Decoding is not verifying
This is the mistake behind most JWT security bugs. Reading the payload tells you what the token claims. It does not tell you the claims are true. Your server must verify the signature with a pinned algorithm, then check exp, nbf, iss and aud, before trusting anything inside. In Node.js with the jsonwebtoken package that looks like this:
import jwt from 'jsonwebtoken';
const claims = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'], // never let the token choose
issuer: 'https://auth.example.com',
audience: 'api.example.com',
clockTolerance: 30 // seconds of allowed clock skew
});
In the browser you may only need to read the payload, for example to show the user's name or schedule a refresh before exp. That is fine as long as nothing security-related depends on it:
function readJwt(token) {
const part = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
const bytes = Uint8Array.from(atob(part), c => c.charCodeAt(0));
return JSON.parse(new TextDecoder().decode(bytes));
}
The TextDecoder step matters. A plain atob breaks names with accents or emoji, because it returns bytes, not UTF-8 text.
Common JWT mistakes
Accepting alg none or letting the token pick the algorithm
A token with "alg":"none" has no signature at all. Older libraries accepted it, and some could be tricked into verifying an RS256 token as HS256 using the public key as the HMAC secret. Always pass an explicit list of allowed algorithms. This decoder flags unsigned tokens in red.
Putting secrets in the payload
Because the payload is only encoded, an email address is fine but a password, API key or internal note is not. If the contents must be hidden, you need an encrypted JWE, not a signed JWT.
Milliseconds instead of seconds
Date.now() returns milliseconds. Writing it straight into exp produces a token that expires tens of thousands of years from now, and some libraries treat the mismatch as invalid. Use Math.floor(Date.now() / 1000).
Weak HMAC secrets
An HS256 token can be brute-forced offline if the secret is a dictionary word. Use at least 32 random bytes, which you can create with the password and key generator, and keep it out of front-end code.
Long-lived tokens with no way to revoke them
A JWT stays valid until exp even after the user logs out. Keep access tokens short (5 to 15 minutes) and use refresh tokens, or keep a deny list of jti values for emergencies.
Frequently asked questions
Is it safe to paste a JWT into an online decoder?
With this one, yes: decoding and verification run entirely in your browser and the token is never sent to a server. As a habit, still avoid pasting live production tokens into tools you have not checked, because a valid token works like a password until it expires.
Can you decode a JWT without the secret?
Yes. The header and payload are only base64url encoded, not encrypted, so anyone holding the token can read them. The secret or public key is needed only to verify that the signature is genuine. That is why you should never put passwords or other sensitive data in a JWT payload.
What is the difference between HS256 and RS256?
HS256 signs with a shared secret using HMAC SHA-256, so every service that verifies tokens also holds the key that can create them. RS256 signs with an RSA private key and verifies with the public key, so verifiers cannot mint tokens. Use RS256 or ES256 when tokens cross service or organization boundaries.
Why does my JWT say expired when it was just issued?
Usually the exp value was written in milliseconds instead of seconds, or the clocks on the issuing and verifying machines disagree. JWT dates are Unix timestamps in seconds. Most libraries accept a small leeway, such as 30 to 60 seconds, to absorb clock skew.
What does alg none mean in a JWT?
It marks an unsigned token. Anyone can create one with any claims they like. A server must never accept alg none; always tell your JWT library exactly which algorithms are allowed when you verify.