How to Read a JWT Token
Step-by-step guide to decoding and reading a JWT token. Learn how to extract the header, payload, and claims — and what you can trust without verifying the signature.
Decode a JWT in 3 Steps
A JWT has three Base64url-encoded sections separated by dots. You can read the header and payload without any secret key — paste the token to decode it instantly.
- Identify the three parts: Split the token on the
.character — you getheader.payload.signature. The header and payload are readable; the signature is binary. - Base64url-decode the header and payload: Each section is Base64url-encoded JSON. Decoding reveals the algorithm used and the claims stored in the token.
- Read the claims: The payload contains key-value pairs —
sub(user ID),exp(expiry),iat(issued at), and any custom claims your app added.
Reading a JWT Without a Tool
- In the browser console:
JSON.parse(atob(token.split('.')[1].replace(/-/g,'+').replace(/_/g,'/')))— decodes the payload to a JavaScript object - In Python:
import base64, json; json.loads(base64.b64decode(token.split('.')[1] + '=='))— the==adds padding that Base64url sometimes omits - Command line:
echo "PAYLOAD" | base64 -d— replace PAYLOAD with the middle section of the JWT (add=padding if needed) - Why the
replacecalls: JWT uses Base64url (-and_instead of+and/). Standardatob()andbase64 -dexpect standard Base64, so you convert the characters first
What You Can and Cannot Trust
- You CAN read without a key: The payload is encoded, not encrypted — any Base64url decode reveals the claims. This is by design: the claims are meant to be readable
- You CANNOT trust without verifying: Anyone can craft a JWT with any payload. Without verifying the signature against a known secret or public key, you have no proof the token was issued by a trusted party
- Safe for debugging: Decoding a JWT to inspect its claims during development is perfectly fine — you're just reading, not trusting
- Never skip verification in production: Always verify the signature server-side before acting on JWT claims in production code. Use a library —
jsonwebtoken(Node.js),PyJWT(Python),golang-jwt/jwt(Go) - Check
expeven after verification: A valid signature doesn't mean the token isn't expired — libraries handle this, but confirm your verification call checks expiry
Frequently Asked Questions
Why does my JWT payload look garbled when I base64 decode it?
JWT uses Base64url encoding, which replaces + with - and / with _, and often omits the = padding characters. Standard Base64 decoders expect +, /, and padding. To fix: replace -→+ and _→/ in the section, then add = padding to make the length a multiple of 4, then decode. The JWT decoder on this page handles all of this automatically.
What's the difference between decoding and verifying a JWT?
Decoding reads the claims by Base64url-decoding the payload — anyone can do this with no key. Verifying checks that the signature matches the header + payload using the secret or public key — only someone with the correct key can verify. In production, you must always verify before trusting claims. Decoding without verification is useful for debugging (inspecting what a token contains) but must never be used to make authorization decisions.
How do I check if a JWT is expired without a library?
Decode the payload and read the exp claim — it's a Unix timestamp (seconds since epoch). Compare to the current time: Date.now() / 1000 > payload.exp in JavaScript returns true if expired. Full one-liner: JSON.parse(atob(token.split('.')[1].replace(/-/g,'+').replace(/_/g,'/'))).exp * 1000 < Date.now(). Remember: this tells you what the token claims about its expiry — only signature verification confirms the claim is trustworthy.