Skip to main content

JWT Decoder

Decode and inspect JWT tokens — header, payload and claims.

Reviewed by · Last reviewed

How to Use the JWT Decoder

  1. Paste your token into the large input area. A JWT is three Base64URL-encoded segments joined by dots, typically starting with eyJ because that is the Base64URL encoding of {".
  2. Click Decode (or wait for the auto-decode to fire on paste). The panel splits into three columns: Header, Payload, and Signature.
  3. Read the header to see the signing algorithm (alg) and token type (typ). Anything other than none means a signature is present.
  4. Inspect the payload to read each claim. Registered claims (iss, sub, aud, exp, nbf, iat, jti) get friendly labels, and timestamp claims are converted from Unix seconds into your local date and time.
  5. Check the expiration badge next to exp to see whether the token is still valid, how long until it expires, or how long ago it expired.
  6. Copy any segment with the copy buttons next to each panel so you can paste decoded JSON into Postman, curl, or your IDE.

What This Tool Does and How It Works

The decoder splits the token on the two dot separators into its three segments, then runs each through a Base64URL decoder that substitutes - and _ back to the standard + and /, re-pads to a multiple of four, and uses atob plus a UTF-8 TextDecoder to produce JSON text. JSON.parse turns that into an object, which Preact renders as a pretty-printed tree. The signature segment is left as raw Base64URL because verifying it would require the server's secret (for HMAC) or public key (for RSA or ECDSA), and exposing those in the browser defeats the purpose of signing. The entire pipeline is pure JavaScript: no fetch, no XHR, no service worker. Your token never crosses the tab boundary.

When You Would Reach for This

  • Debugging a 401 response from your API and you need to see whether the aud claim actually matches the expected audience.
  • Checking that a newly minted access token carries the roles or scopes your authorization middleware is looking for.
  • Confirming that your identity provider is encoding custom claims under the namespace you configured in Auth0, Okta, Keycloak, or Cognito.
  • Reading the exp claim to confirm a short-lived token really is short-lived before shipping it to production.
  • Teaching a teammate why storing PII in the payload is a bad idea - the payload is signed, not encrypted, and anyone with the token can read it.
  • Diffing the claims in a working token against a broken one when integrating with a new partner.

Common Pitfalls and Edge Cases

  • Confusing JWT with JWE. A five-segment token (four dots) is an encrypted JWE and cannot be read without the decryption key. This decoder only handles three-segment JWS.
  • Padding mistakes when copying. Some loggers truncate the signature. The header and payload still decode, but your downstream verifier will reject the token.
  • Clock skew. A token that looks expired by one second may still be accepted by the server thanks to a leeway window (usually 30-60 seconds). The UI shows your local time, which may drift from the issuer's clock.
  • Nested JSON in claims. Some IdPs serialize roles as a JSON string inside a claim rather than an array. The decoder displays the raw string - parse it a second time to inspect the array.
  • UTF-8 in the payload. Claims with non-ASCII text (names, labels in other scripts) rely on correct UTF-8 decoding. The tool uses TextDecoder("utf-8") so multi-byte sequences and surrogate pairs survive the round trip.

JWT Format in a Nutshell

JSON Web Tokens are defined by RFC 7519, which builds on JWS (RFC 7515) for signing and JWA (RFC 7518) for algorithm identifiers. A JWS compact serialization is BASE64URL(header) "." BASE64URL(payload) "." BASE64URL(signature). The header object must include alg (the signing algorithm such as HS256, RS256, ES256, or EdDSA) and usually includes typ: "JWT". The payload is a set of claims. Seven names are registered in RFC 7519 itself; public claims live in the IANA JSON Web Token Claims registry; private claims are whatever your application invents. Importantly, a JWT is signed, not encrypted - everything except the signature is in the clear, and anyone can Base64URL-decode it. Treat JWTs as bearer tokens: possession is authorization.

Comparison to Alternatives

On the command line you can achieve the same decoding with cut -d. -f2 <<< "$TOKEN" | base64 -d | jq, but that is fiddly with Base64URL padding and does not surface expiration status. Dedicated CLIs like jwt-cli (cargo install jwt-cli) and step jwt inspect from Smallstep are excellent when you already have a terminal open. IDE plugins such as the JWT extension for VS Code inline-decode tokens in your editor. Browser extensions exist but typically read page content - a real risk if you have work tokens in localStorage. For verifying signatures you still need a backend or a library like jose, pyjwt, or jsonwebtoken, because only they have the key. Use this page when you need a quick read-only inspection without installing anything.

Frequently Asked Questions

What is a JSON Web Token in one paragraph?

A JWT is a compact, URL-safe string that packages claims as JSON and attaches a cryptographic signature proving the issuer created it. It consists of a header describing the algorithm, a payload with the claims, and a signature over the first two segments. Because it is self-contained and signed, a service can validate it without calling back to the issuer, which is why it dominates stateless APIs. RFC 7519 is the governing spec.

Does this decoder verify the signature?

No, and deliberately so. Verification requires the issuer secret (HS256) or the public key at a JWKS endpoint (RS256, ES256, EdDSA). Fetching those would ship your token over the network. Use a server-side library such as jose, jsonwebtoken, pyjwt, or jjwt to verify; this tool is strictly a read-only inspector.

Is it safe to paste production tokens into this page?

Decoding runs entirely in your browser tab with no fetch call and no analytics beacon receiving the token. That said, tokens are bearer credentials - anyone with your screen or clipboard history can impersonate you until the token expires. Revoke or rotate any token you suspect was exposed, and prefer a staging token when demonstrating this tool.

Why do timestamp claims show as dates instead of numbers?

exp, iat, nbf, and auth_time are NumericDate values defined in RFC 7519 as seconds since the Unix epoch. The decoder multiplies by 1000, passes to Date, and formats with Intl.DateTimeFormat in your locale. The raw integer is shown alongside for copy-paste.

What algorithms can appear in the alg header?

RFC 7518 registers HS256/384/512 (HMAC-SHA-2), RS256/384/512 (RSASSA-PKCS1-v1_5), PS256/384/512 (RSASSA-PSS), ES256/384/512 (ECDSA), and EdDSA (Ed25519/Ed448, RFC 8037). alg: none indicates unsigned - historically a source of severe vulnerabilities and rejected by modern libraries.

Why does my token start with eyJ?

The JWT header is a JSON object that starts with an opening brace and a quoted key, for example the two bytes {&quot;. When Base64URL-encoded, those bytes become eyJ, which is why virtually every JWT you will ever see starts with those three letters. It is a useful sniff test: if a supposed JWT does not start with eyJ, it is probably a different token format or has been mangled in transit.

How long should a JWT live before expiring?

OWASP guidance: access tokens short-lived (5-60 minutes), rotated using refresh tokens in secure storage. Long-lived JWTs are hard to revoke because they are self-contained - without a deny list there is no way to invalidate one before its exp. A 30-day exp is a design smell.

What is the difference between the kid header and the sub claim?

The kid (key ID) header lives in the header segment and tells the verifier which public key, out of possibly many published at a JWKS endpoint, was used to sign this token. The sub (subject) claim lives in the payload and identifies the principal the token is about - usually a user ID or service account. They are unrelated; kid is for key rotation on the issuer side, sub is the business identifier on the consumer side.

Can a JWT contain binary data?

Not directly. The payload must be a valid JSON object per RFC 7159, and JSON has no binary type. If you need to ship bytes (a certificate thumbprint, an image hash), Base64URL-encode them into a string claim. Because the token itself is already Base64URL-encoded, you end up with double encoding, so keep payloads small; a few hundred bytes is typical and tens of kilobytes starts to cause HTTP header size problems.

Why is the payload readable without a key?

JWS (RFC 7515) provides integrity but not confidentiality. The payload is Base64URL-encoded, not encrypted, so anyone who captures the token can read it. To hide contents from intermediaries use JWE (RFC 7516), which produces a five-segment token and encrypts the payload. JWE cannot be decoded here.

How is this different from running base64 in a terminal?

Manually decoding with base64 requires you to split on dots, fix URL-safe alphabet substitutions, add padding back, and then pretty-print the JSON yourself - usually with jq. This page does all of that automatically, labels the registered claims, converts NumericDate values to human-readable times, and flags expiration status. For a quick one-off check it is faster; for scripting many tokens, pair jwt-cli with jq instead.

More Developer Tools