How to decode a JWT
- Paste the token (with or without the “Bearer ” prefix) into the Encoded token box.
- The header and payload appear decoded, with time claims shown as dates and an expiry check.
- To verify it, paste the HMAC secret (HS256/384/512) or PEM public key (RS, PS or ES algorithms).
What's inside a JWT
A JWT is three Base64URL strings joined by dots: header.payload.signature.
| Part | Contains | Example |
|---|---|---|
| Header | The signing algorithm and token type | {"alg":"HS256","typ":"JWT"} |
| Payload | The claims: who the token is for, when it expires, permissions | {"sub":"123","exp":1792886400} |
| Signature | A signature over the first two parts, made with a secret or private key | 3GqyuvYk-5I3Ym… |
Signing algorithms
| Algorithm | Key | Who can verify |
|---|---|---|
| HS256 / HS384 / HS512 | One shared secret (HMAC) | Anyone with the secret, which can also create tokens |
| RS256 / PS256 | RSA private key signs, public key verifies | Anyone with the public key |
| ES256 / ES384 | Elliptic-curve key pair (smaller signatures) | Anyone with the public key |
| none | No signature | Never accept these |
Decode a JWT in code
// Decode (not verify) a JWT in the browser or Node.js 16+
function decodeJwt(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));
}
// Node.js only: Buffer understands base64url directly
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString());import jwt # pip install PyJWT
# Read the claims without checking the signature (debugging only)
claims = jwt.decode(token, options={"verify_signature": False})
# Verify properly: checks the signature, exp and nbf
claims = jwt.decode(token, "your-secret", algorithms=["HS256"])import "github.com/golang-jwt/jwt/v5"
// Read the claims without verifying (debugging only)
claims := jwt.MapClaims{}
_, _, err := jwt.NewParser().ParseUnverified(tokenString, claims)
// Verify with an HMAC secret
token, err := jwt.Parse(tokenString, func(t *jwt.Token) (any, error) {
return []byte("your-secret"), nil
}, jwt.WithValidMethods([]string{"HS256"}))# Decode the payload with jq
jq -R 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson' <<< "$TOKEN"JWT security checklist
- Always verify the signature on the server, and pin the expected algorithm (never trust the token's own alg).
- Check exp, nbf, iss and aud, not just the signature.
- Keep expiry short (minutes to hours) and use refresh tokens for longer sessions.
- Don't put passwords, card numbers or other secrets in the payload: anyone can read it.
Calling an API that returns 401 Unauthorized? Decode your token here to check it hasn't expired. More tools: Base64 decoder, Unix timestamp converter.