Skip to content

JWT Decoder

Decode and inspect a JWT token (no signature verification).

A JSON Web Token (JWT) is a compact, URL-safe string that carries claims about a user or session, signed by the issuer so the receiver can verify it wasn’t tampered with. A JWT has three parts separated by dots: header, payload, signature.

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJleHAiOjE3MzAwMDAwMDB9.5RZ3...
└── header ────────┘ └── payload ─────────────────────────┘ └ sig ┘

The three segments

Header. A small JSON object that names the signing algorithm (alg) and token type (typ), then base64url-encoded. A typical header decodes to:

{ "alg": "HS256", "typ": "JWT" }

Payload. A JSON object of claims — facts the issuer wants to assert. Standard claims include sub (subject, usually a user ID), iat (issued-at timestamp), exp (expiration timestamp), aud (intended audience), and iss (issuer). Custom claims sit alongside the standard ones. The payload is base64url-encoded, not encrypted.

Signature. A keyed hash of base64url(header) + "." + base64url(payload), computed with the algorithm named in the header. With HS256 the key is a shared secret; with RS256 it’s the issuer’s private RSA key, and verifiers use the matching public key. The signature is what makes the token tamper-evident: change a single character of the payload and the signature won’t match anymore.

What a JWT isn’t

It isn’t encrypted. Base64url-encoded is not encrypted. Anyone with the token can decode it and read every claim in the payload — including, if you put it there, an email address, an internal user ID, a permission set, or anything else. Treat the payload as readable.

It isn’t a session. A session lives on the server and can be revoked by deleting it. A JWT is self-contained: once issued, it’s valid until exp. Revoking a leaked JWT before its expiration requires either a denylist (which gives back the server-state you avoided by using JWTs) or short expirations with refresh-token rotation.

It isn’t proof the user did anything. A valid signature proves the issuer minted the token. It does not prove the human at the keyboard authenticated recently — only that they presented a token the issuer signed at some earlier point.

Pitfalls

  • Storing JWTs in localStorage exposes them to any XSS on the origin. Cookies with HttpOnly; Secure; SameSite=Strict are the lower-risk default for browser sessions.
  • Long expirations. A 30-day exp means a stolen token is valid for 30 days. Short-lived access tokens (5–15 minutes) plus refresh tokens are the standard mitigation.
  • Trusting the alg header. Some libraries historically accepted alg: none from the header itself, allowing forged tokens. Always pin the expected algorithm in the verifier; never let the token tell you how to verify itself.
  • Putting secrets in claims. Anything in the payload is readable. PII, internal infrastructure identifiers, and permission scopes are common over-shares.
  • Skipping signature verification. Decoding a JWT (which is just base64url) is not the same as verifying it. A debug logger that calls jwt.decode() instead of jwt.verify() can be the source of a “we trusted an unsigned token” incident.

Use the JWT decoder above to inspect a token’s contents — the decoder runs locally in your browser, so the token never leaves your machine. To generate signed test tokens for development, see the JWT generator.