How to Decode a JWT and Read Its Claims Safely
A JWT looks like gibberish, but it is just three Base64URL segments hiding plain JSON. This guide walks through decoding a token, reading its claims, and avoiding the mistakes that cause auth bugs.
A JSON Web Token looks like a wall of random characters, but it is not encrypted and it is not random - it is three Base64URL-encoded segments separated by dots, and two of them are just JSON. Once you know that, decoding one is trivial. Understanding what the claims inside actually mean, and what decoding does and does not prove, is the part that trips people up during an auth debugging session.
This guide walks through how a JWT is put together, how to pull it apart, and the mistakes that cause "why is this token rejected" bugs. You will also see how to do it instantly with the JWT Decoder - a free tool that decodes the header and payload entirely in your browser, so tokens carrying real user data never leave your machine.
What is a JWT, really?
A JSON Web Token is a compact string made of three parts joined by dots: header.payload.signature. The header and payload are each a JSON object that has been Base64URL-encoded - the URL-safe variant of Base64 that swaps +// for -/_ and usually drops the trailing = padding so the token is safe to put in a URL or an HTTP header without escaping.
The header typically identifies the signing algorithm (alg) and token type (typ), and sometimes a key id (kid) that tells a verifier which key to use. The payload holds the claims - statements about the user or session, such as who issued the token, who it belongs to, and when it expires. The signature is a cryptographic value computed over the header and payload; it is what lets a server trust that the claims have not been altered.
A JWT and its decoded parts
Three dot-separated segments become a readable header and payload once decoded.
Input
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfODgxMSIsIm5hbWUiOiJEYW5hIEsiLCJhdWQiOiJhcHAubG9jYWxvbmx5LmRldiIsImV4cCI6MTc0NTQyODgwMH0.4f2c9b1a7e6d8f0a3c5b2e9d1a4f6c8b0e2d4a6c8f0b2d4e6a8c0f2b4d6e8f0Output
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"sub": "usr_8811",
"name": "Dana K",
"aud": "app.localonly.dev",
"exp": 1745428800
}Why decoding a JWT matters
You do not need a server or a library to read what a token claims - decoding is just reversing a text encoding. That matters constantly during day-to-day development:
- Faster auth debugging - see immediately whether a
401is caused by an expired token, the wrong audience, or a missing scope. - Sanity-checking an identity provider - confirm your OAuth or SSO integration is actually issuing the claims your backend expects.
- Spotting expired sessions - convert
expandiatto real dates instead of squinting at Unix timestamps. - Reviewing scope creep - check exactly which roles, scopes, or permissions a token grants before wiring up an authorization check.
How JWT decoding works under the hood
Decoding is a two-step reversal of how the token was built: split the string on its dots, then Base64URL-decode the first two segments. The result of each is a UTF-8 string containing JSON, which a decoder then parses and pretty-prints so it is readable. The signature segment is not JSON - it is raw bytes - so a decoder shows it as-is rather than trying to parse it.
A detail that catches people out: exp, iat, and nbf are NumericDate values, meaning seconds (not milliseconds) since the Unix epoch. Read raw, 1745428800 tells you nothing useful. Converted, it is an exact expiry moment you can compare against the current time - which is exactly what the JWT Decoder does automatically, flagging whether a token is currently valid, expired, or not yet active.
Where does your token go?
JWT payloads routinely carry user IDs, email addresses, roles, and internal permissions - exactly the kind of data you should not paste into a random online tool. The JWT Decoder does all of this in your browser: the token is split, Base64URL-decoded, and parsed locally. Nothing is uploaded, logged, or stored, so it is safe to inspect real production tokens while debugging.
Decoding is not verifying
Decoding only reveals what a token claims - it does not prove the claims are genuine. Verifying the signature requires the issuer's secret or public key and must happen server-side with a proper JWT library. Never make an authorization decision based on a decoded-but-unverified payload.
Step-by-step: decode a JWT and read its claims
- Open the JWT Decoder and paste the full token - all three dot-separated segments.
- Check the header for
alg(the signing algorithm) andkid(which key was used), useful context before you go configure verification elsewhere. - Read the payload's claims, pretty-printed as formatted JSON, with
exp,iat, andnbfshown as human-readable dates. - Check the expiry indicator to see at a glance whether the token is currently valid, expired, or not yet active.
- Copy any claim value you need -
sub,aud, a custom role field - straight into your bug report or debugger.
That is the entire loop. No account, no server round-trip, and it keeps working even if you paste in a token for a service that is currently down.
Common use cases
- Debugging why a request returns 401 or 403 during local development.
- Confirming a token's expiry and issued-at time while chasing down a session bug.
- Checking which scopes, roles, or audience a token actually carries before writing an authorization check.
- Inspecting the
kidandalgheader fields before wiring up signature verification. - Understanding an unfamiliar identity provider's claim structure the first time you integrate it.
Common mistakes and how to avoid them
Assuming a JWT is encrypted
A standard signed JWT is not encrypted - it is Base64URL-encoded, which anyone can reverse in seconds, whether with a purpose-built tool or a one-line script using Base64 Decode. The signature protects against tampering, not disclosure. Never put a password, API key, or other secret inside a JWT payload; assume anyone holding the token can read every claim.
Trusting a decoded payload without verification
It is tempting to decode a token client-side and use its claims directly for an authorization decision. Don't. Anyone can craft a JSON object and Base64URL-encode it; only a valid signature - checked against the issuer's key - proves the claims were not forged. Decoding is for reading and debugging; verification is a separate, security-critical step.
Confusing seconds and milliseconds
exp, iat, and nbf are Unix seconds, but most JavaScript date APIs expect milliseconds. Feeding a raw claim straight into new Date() produces a date decades in the past. Multiply by 1000 first, or let a decoder do the timestamp conversion for you.
Best practices for working with JWTs
- Treat every real token as a credential - only paste tokens you are authorized to inspect, and prefer expired or test tokens when sharing screenshots.
- Always verify the signature server-side with a proper library before trusting any claim for access control.
- Check
expandnbfexplicitly rather than assuming a token is valid just because it decodes cleanly. - Use a short expiry for access tokens and rely on refresh tokens for longer sessions, so a leaked token has a small blast radius.
- When debugging, decode locally rather than pasting tokens into a third-party service that may log what you send.
Related tools
Decode Base64 back to readable text, including URL-safe input.
Compute SHA-1, SHA-256, SHA-384, and SHA-512 hashes of text or files.
Convert between Unix timestamps and human-readable dates, both ways.
Pretty-print JSON with configurable indentation and instant validation.
Related guides
Base64 encoding turns bytes into safe text, and decoding undoes that step. This guide covers how decoding works, why URL-safe tokens need extra care, and how to recover the original text without uploading it anywhere.
Cryptographic hashes let you fingerprint text or files and verify they haven't changed. Here's how SHA-1, SHA-256, SHA-384, and SHA-512 work, and how to compute them without uploading anything.
Unix timestamps look simple until you hit a 13-digit number that breaks your date parser. This guide explains epoch time, common conversion mistakes, and how to convert timestamps to dates (and back) without uploading anything.
Minified JSON is unreadable and hard to debug. This guide covers how JSON formatting works, when to use it, common mistakes, and how to pretty-print JSON without uploading it anywhere.