Decoding JWTs Safely in the Browser
Why client-side JWT inspection never leaks secrets, and how to read claims without a network round-trip.
The header and payload of a JSON Web Token are only Base64URL-encoded, not encrypted. That means you can inspect them entirely in the browser without ever sending the token to a server. The JWT decoder on this site runs 100% client-side — nothing is transmitted.
The three parts of a JWT
A JWT is three Base64URL strings joined by dots:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhYmMxMjMifQ.signature
- Header — the algorithm and token type.
- Payload — the claims (
sub,exp,iat, custom ones). - Signature — the only part that proves integrity.
Only the signature is cryptographically meaningful, and verifying it requires the secret/public key. Decoding the first two segments reveals nothing an attacker couldn't read themselves.
Reading the expiry claim
The exp claim is a Unix timestamp in seconds. Compare it to Date.now() / 1000:
function isExpired(token: string): boolean {
const [, payload] = token.split('.')
const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')))
return typeof decoded.exp === 'number' && decoded.exp * 1000 < Date.now()
}
Never trust claims for authorization on the client. Decode for display and debugging only — the server must always re-verify the signature.
What stays private
The signing secret, refresh tokens, and any server-side authorization logic never touch the browser. That is the trust boundary: the token's contents are public, its validity is not.