Decode JWT tokens to inspect headers, payload claims, and expiration times. Understand the three-part structure โ€” header, payload, signature โ€” and learn which standard claims to check (exp, iat, sub, iss, aud). Debug authentication failures by decoding tokens instantly in your browser with a free JWT decoder.

How to Decode JWT Tokens and Understand Their Structure

How to Decode JWT Tokens and Understand Their Structure

If you work with web APIs, you have seen JWTs โ€” those long strings with three dot-separated sections. They carry authentication claims, but they are encoded, not encrypted. Anyone can decode the payload. Here is how to inspect JWT tokens to debug auth issues, verify claims, and understand what your application is actually transmitting.

JWT structure: header, payload, signature

A JWT has three parts: the header (algorithm and token type), the payload (claims like user ID, roles, expiration), and the signature (which verifies the token has not been tampered with). The first two parts are Base64URL-encoded JSON. The ToolStand JWT Decoder splits the token, decodes each section, and displays the header and payload as formatted JSON. The signature section is shown but cannot be decoded without the secret key.

Key claims to check

exp (expiration). The Unix timestamp when the token expires. If it is in the past, the token is invalid. iat (issued at). When the token was created. sub (subject). The user ID or principal the token represents. iss (issuer). Who issued the token โ€” should match your auth server. aud (audience). Who the token is intended for โ€” should match your application.

Debugging with the decoder

When a user reports "I am logged in but getting 403 errors," paste their JWT into the decoder. Check the exp claim โ€” if the token expired 5 minutes ago, that is your answer. Check the roles or permissions claim โ€” maybe they do not have the required scope. The decoder turns a black-box token into readable data in seconds.

Security reminder

Decoding a JWT reveals its contents but does not verify its signature. Never trust a JWT payload unless you have verified the signature with the correct secret or public key. The decoder is a debugging tool, not a validation tool.

Base64URL decoding โ€” what happens inside the decoder

JWT sections use Base64URL encoding, not standard Base64. The difference: Base64URL replaces + with -, / with _, and strips trailing = padding. If you try to decode a JWT with a standard Base64 decoder, the + and / characters will produce garbage output. The JWT Decoder handles this internally. In JavaScript, the decode step is: JSON.parse(atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"))) โ€” the replace calls undo the URL-safe encoding before atob() can process it. In Python: base64.urlsafe_b64decode(payload + "==") adds back the padding. Understanding this encoding quirk explains why pasting a JWT into a generic Base64 decoder sometimes fails.

The signature is not magic โ€” how RS256, HS256, and ES256 differ

The alg claim in the JWT header tells you which signature algorithm was used, and choosing wrong has security consequences. HS256 (HMAC-SHA256): uses a shared secret. Anyone who knows the secret can both create and verify tokens. If your secret is weak (e.g., "secret123"), an attacker can forge tokens. RS256 (RSA-SHA256): uses a public/private key pair. The private key signs tokens; the public key verifies them. The public key can be shared openly without risk. ES256 (ECDSA-P256): similar to RS256 but with smaller, faster keys. The critical insight: a JWT decoder reveals the algorithm but cannot verify the signature without a key. If a token claims RS256 but its signature does not match the public key, it is forged. The decoders alg field tells you what verification method to use โ€” a first debugging step before checking expiration and claims.

The "none" algorithm attack โ€” a real-world vulnerability

In 2015, security researchers discovered that many JWT libraries accepted tokens with {"alg": "none"} in the header. This tells the verifier to skip signature checking entirely โ€” accepting any payload as valid. The attack works when the server blindly trusts the algorithm declared by the client. Production JWT libraries now reject "none" by default, but custom implementations and older frameworks are still vulnerable. When you decode an unknown JWT, check the alg field: if it says "none," the token has no signature at all. The JWT Decoder displays the algorithm prominently so you can spot this immediately.

Advanced claims โ€” nbf, jti, and custom claims beyond the basics

Beyond exp, iat, sub, iss, and aud, several standard claims deserve attention. nbf (not before): the token is invalid before this timestamp. This is used for post-dated tokens โ€” for example, an access token that becomes valid at midnight. jti (JWT ID): a unique identifier for this specific token. Used with a denylist to revoke individual tokens without invalidating all sessions. Custom claims: applications can add any key-value pair to the payload, such as "role": "admin", "tenant_id": "abc-123", or "permissions": ["read", "write"]. When debugging 403 errors, comparing the custom claims between a working and failing token reveals permission differences immediately.

JWT vs. opaque tokens vs. session cookies โ€” when to use each

JWTs are stateless: the server does not need to look up a session in a database to validate them. This makes them ideal for microservices and distributed systems where a database round-trip is expensive. But statelessness has costs: you cannot revoke a single JWT without maintaining a denylist (which defeats the stateless benefit), and the token grows with each claim, inflating HTTP headers. Opaque tokens: random strings that require a server-side lookup to validate. Instantly revocable but slower at scale. Session cookies: tied to a browser, protected by HttpOnly and SameSite flags, automatically invalidated when the browser closes. The JWT Decoder helps you inspect JWTs; for opaque tokens, use your auth server admin panel. Choose JWTs when you need stateless, cross-service authentication; choose opaque tokens or sessions when you need instant revocation.

Frequently Asked Questions

Is it safe to decode a JWT in a browser-based tool?

Yes, for the header and payload โ€” these are not encrypted, only encoded. Anyone who has the token can decode it. The signature section cannot be decoded without the secret key, and the decoder never transmits your token to any server. All decoding happens locally in your browser. The security risk is not the decoding itself, but copying tokens from production environments: treat JWTs like passwords and clear them from the decoder after use.

Why does my JWT have fewer parts than expected?

A standard JWT has three dot-separated parts (header.payload.signature). If you see only two (header.payload), it might be a JWS with detached content or a different token format entirely. If the signature section is missing, the token is unsigned and should not be trusted. The decoder will flag malformed tokens.

What does "iat" mean and why does it matter?

iat stands for "issued at" โ€” the Unix timestamp when the token was created. Servers use it with the exp claim to calculate the token lifetime window. If iat is in the future, the server clock is likely skewed. If it's far in the past, the token may be a stale copy. Checking iat alongside exp tells you the token's full validity window.

Can I modify a JWT payload and re-encode it?

You can modify the decoded payload and re-encode it, but the resulting token will fail signature verification because you do not have the signing key. The signature is computed over the encoded header and payload โ€” changing either invalidates it. This is by design: the signature prevents tampering. The decoder lets you edit the payload for experimentation, but the result will not authenticate against any properly configured server.

How do I verify a JWT signature without a decoder?

You need the public key (for RS256/ES256) or the shared secret (for HS256). In Python: jwt.decode(token, public_key, algorithms=["RS256"]) using the PyJWT library. In Node.js: jwt.verify(token, publicKey, { algorithms: ["RS256"] }) using jsonwebtoken. The decoder confirms the algorithm and structure; actual verification requires a library and the correct key.

What is the difference between JWT, JWS, and JWE?

JWT (JSON Web Token) is the general container format. JWS (JSON Web Signature) is a signed JWT โ€” what most people mean when they say "JWT." JWE (JSON Web Encryption) is an encrypted JWT where the payload is not readable without decryption. If you paste a JWE into a standard JWT decoder, you will see garbled ciphertext, not JSON. The JWT Decoder handles signed tokens; encrypted tokens require the decryption key.

Explore all 109 free tools at toolstand.io. Free, forever. No sign-up. No download. Just tools that work.