JWT Decoder

Paste a JWT and instantly see its header, payload and signature.

..
{{ __t('section_header') }}

                        
{{ __t('section_payload') }}


                            
{{ __t('claims_heading') }}
{{ __t('section_signature') }}

                            

{{ __t('signature_note') }}

{{ __t('empty_state') }}

What is a JWT?

A JSON Web Token (JWT, RFC 7519) is an open standard for securely transmitting claims between two parties in a compact, URL-safe format. JWTs are used in modern web APIs for authentication and authorization — for example, after login to identify the user on every subsequent request.

Important: A JWT is not encrypted, only signed. Anyone who possesses the token can read its contents. Never store passwords or sensitive data in the payload.

Structure of a JWT

  • Header — describes the token type (typ) and the signing algorithm (alg), e.g. HS256, RS256.
  • Payload — contains the claims (data points) as JSON, base64url-encoded. Both standard and custom claims are allowed.
  • Signature — cryptographic signature over header + payload, created with the algorithm specified in alg.

Standard claims (RFC 7519)

  • iss — Issuer (who issued the token)
  • sub — Subject (typically the user ID)
  • aud — Audience (intended recipient of the token)
  • exp — Expiration Time (expiry as a Unix timestamp)
  • iat — Issued At (when the token was issued)
  • nbf — Not Before (not valid before this time)

Is this tool secure?

Yes. Decoding happens entirely in your browser with JavaScript. No token is sent to any server and nothing is logged. You can inspect the implementation in your browser's developer tools at any time. Still, only paste production tokens in trusted environments.

How a JWT is validated

A JSON Web Token is defined by RFC 7519 as a compact URL-safe representation of claims. The three parts header.payload.signature are each Base64URL-encoded (RFC 4648 §5) — not classic Base64 with + and / but - and _, and without trailing = padding. Decoding therefore means: split on dots, Base64URL-decode each part, parse JSON. That alone validates NOTHING — you only see what is inside the token. Real validation requires three steps: verify the signature, verify the claims, verify the algorithm.

The signature covers the first two segments as base64url(header) + "." + base64url(payload) using the algorithm declared in the alg header claim. HS256 uses HMAC-SHA-256 with a shared secret, RS256 uses RSA-PKCS1-v1.5 with SHA-256 (private key to sign, public to verify), ES256 uses ECDSA P-256, PS256 uses RSASSA-PSS, EdDSA uses Ed25519. When verifying you MUST restrict alg to a whitelist — otherwise an attacker can bypass the signature with alg: none or by swapping the algorithm (CVE-2015-9235, the famous HS256/RS256 confusion bug).

After signature verification comes claim validation. exp (Expiration Time, seconds since Unix epoch) must be in the future, nbf (Not Before) in the past, iat (Issued At) should be plausible. RFC 7519 recommends a leeway window of a few seconds for clock skew between issuer and verifier — most libraries (jose, jjwt, PyJWT) expose this as a parameter. iss (Issuer) and aud (Audience) you MUST check yourself, otherwise you accept foreign tokens: a token issued for service A must not be valid at service B, even if both have the same issuer.

Header and payload fields in detail

These fields appear in nearly every production JWT. Other claims are allowed but may be implementation-specific.

  • Header alg: signature algorithm, e.g. HS256, RS256, ES256, EdDSA, none (never accept). Defined in RFC 7518 (JWA).
  • Header kid: key ID. Lets the verifier pick the right key from a key set — mandatory for key-rotation setups (e.g. JWKS endpoints in OAuth2/OIDC).
  • Payload exp / nbf / iat: NumericDate values in seconds since the Unix epoch. 1700000000 = 2023-11-14 22:13:20 UTC.
  • Payload iss / aud / sub: issuer, audience, subject. aud can be a string OR an array, which is a common parser-bug source — always handle both shapes.
  • Payload jti: unique token ID. Mandatory for any token blacklist or revocation system; ideally a UUIDv4 or cryptographically secure random string.

Example tokens and what they mean

These header and payload examples show which fields you actually see in real APIs and what they mean.

  • {"alg":"HS256","typ":"JWT"} — standard header for symmetric signing, typical for self-hosted auth servers or frameworks like Devise (Rails) and Laravel Sanctum.
  • {"alg":"RS256","typ":"JWT","kid":"abc-2024-01"} — asymmetric with key rotation. The kid points to a public key in the JWKS at /.well-known/jwks.json.
  • {"sub":"42","iat":1700000000,"exp":1700003600,"iss":"https://auth.example.com","aud":"api.example.com"} — one-hour access token for user 42 issued by a central auth server.
  • {"sub":"user-7","scope":"read:profile write:posts","exp":...} — OAuth2 scope claim with a space-separated list. Auth servers like Auth0, Keycloak and Okta use this format.
  • OIDC ID Token: {"iss":"https://accounts.google.com","sub":"110...","aud":"client-id","email":"[email protected]","email_verified":true,"nonce":"..."} — the nonce must match the one the client sent.

Security risks and known bugs

JWT is secure when used correctly — and insecure when not. The classic first bug is alg: none: some libraries accept a token without signature if the header says so. Whitelist algorithms strictly on the server side. Second bug: HS256/RS256 confusion. If the verifier uses the RSA public key as an HMAC secret, an attacker can sign a token with alg: HS256 that "verifies" against the public key. Third bug: no revocation. JWT is deliberately stateless — once issued, valid until exp. If you need logout or token revocation, maintain a blacklist by jti or use short exp + refresh tokens. Fourth bug: too many claims. JWTs end up in cookies, Authorization headers and logs — every claim adds size and pushes you against HTTP header limits (typically 8 KB in nginx). Personal data does not belong in a bearer token because it flows to every resource server that sees it. Fifth bug: no TLS. A JWT without HTTPS is effectively a plaintext session token — whoever captures it is the user.

Frequently asked questions about JWT

How do I verify a JWT on the server side?
Use a library like jose (Node), PyJWT (Python), jjwt (Java) or firebase/php-jwt (PHP). Important: set the algorithms parameter explicitly, never trust the token's own alg header. Fetch the public key/secret externally, ideally via JWKS.
Can I expire a JWT before its exp reaches now?
Not directly — JWT is stateless by design. Workarounds: a server-side blacklist keyed by jti, or a global "min issued at" timestamp per user that invalidates all earlier tokens. Both undo the stateless benefit.
Where do I store a JWT in the browser?
Safest is an HttpOnly cookie with Secure and SameSite=Lax, because JavaScript cannot read it (XSS-safe). LocalStorage is vulnerable to XSS but is the default in many SPA architectures. Never in the URL — it ends up in server logs, Referer headers and browser history.
What does the error "jwt malformed" mean?
The token does not have the header.payload.signature shape with exactly two dots. Common causes: whitespace, a duplicated Bearer prefix in the Authorization header, token truncated due to header-size limits or an empty string because the cookie was not set yet.
What is the difference between JWT and OAuth2?
OAuth2 (RFC 6749) is an authorization framework that issues tokens to clients. JWT (RFC 7519) is a token format. Many OAuth2 providers use JWT as the access token format, but OAuth2 does not require it — tokens can be opaque strings. Conversely, JWT can be used entirely outside OAuth2, e.g. for internal service-to-service calls.
Are my tokens stored by this tool?
No. The decoder runs entirely in the browser. The token never leaves your machine, nothing is logged. You can inspect the implementation in DevTools or load the page offline. Still: never paste production tokens into third-party online tools whose source you cannot audit yourself.

Related tools