If you have spent any time in modern web development, you have almost certainly encountered JSON Web Tokens (JWTs). They are everywhere — authentication headers, API keys, OAuth flows, single sign-on systems. Yet many developers use them without fully understanding what is inside the token they are passing around. A JWT decoder peels back the layers of a token so you can inspect its headers, payload, and signature. In this guide, we will break down exactly how JWTs work, how to decode them, what each part means, and what security pitfalls to watch for.
Important: Decoding a JWT is not the same as verifying it. Anyone can decode a JWT — the payload is just base64-encoded, not encrypted. Verification requires the secret key or public key that was used to sign the token.
A JSON Web Token is a compact, URL-safe string that represents a set of claims (statements about an entity, typically the user). It is defined by RFC 7519 and has become the de facto standard for token-based authentication in web applications.
The structure is elegantly simple: three base64url-encoded segments separated by dots.
xxxxx.yyyyy.zzzzz
The three parts are:
This compact format is what makes JWTs so popular. They fit neatly into HTTP headers, URL parameters, and cookies. A typical JWT is only a few hundred characters long, yet it can carry rich structured data.
The header is a JSON object that tells the receiver how to process the token. It typically contains two fields:
{
"alg": "HS256",
"typ": "JWT"
}
alg — the signing algorithm. Common values include HS256 (HMAC with SHA-256), RS256 (RSA with SHA-256), and ES256 (ECDSA with SHA-256).typ — the token type. Almost always JWT, but some implementations omit it.When decoded, the header tells you exactly how the signature was generated, which is critical for verification.
The payload contains the claims — the actual data the token carries. Claims come in three categories:
iss (issuer) — who created the tokensub (subject) — who the token is about (usually the user ID)aud (audience) — who the token is intended forexp (expiration) — when the token expires (Unix timestamp)nbf (not before) — when the token becomes validiat (issued at) — when the token was createdjti (JWT ID) — a unique identifier for the tokenname, email, role){
"sub": "user_12345",
"name": "Jane Doe",
"email": "jane@example.com",
"role": "admin",
"iat": 1713139200,
"exp": 1713225600
}
A common misconception is that JWT payloads are encrypted. They are not. Anyone who intercepts the token can decode the payload and read the claims. This is why you should never put sensitive information (passwords, credit card numbers, Social Security numbers) in a JWT payload.
The signature is what makes a JWT tamper-proof. It is created by combining the header and payload with a secret key using the algorithm specified in the header.
HMACSHA256( base64urlEncode(header) + "." + base64urlEncode(payload), secret )
When the server receives a token, it recomputes the signature using its own copy of the secret key. If the computed signature matches the one in the token, the payload has not been modified. If they do not match, the token has been tampered with and should be rejected.
For asymmetric algorithms like RS256, the server signs with a private key and the client verifies with the corresponding public key. This is particularly useful in microservice architectures where multiple services need to verify tokens without sharing a secret.
Decoding a JWT is straightforward because the header and payload are simply base64url-encoded. You can decode them in any programming language or even in your browser's console.
function decodeJWT(token) {
const parts = token.split('.');
const header = JSON.parse(atob(parts[0].replace(/-/g, '+').replace(/_/g, '/')));
const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
return { header, payload };
}
const result = decodeJWT('eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NSJ9.abc123');
console.log(result);
import base64, json
def decode_jwt(token):
parts = token.split('.')
header = json.loads(base64.urlsafe_b64decode(parts[0] + '=='))
payload = json.loads(base64.urlsafe_b64decode(parts[1] + '=='))
return {'header': header, 'payload': payload}
Again, decoding is not verifying. These functions will happily decode a token with an invalid or missing signature. Verification requires the signing key and is typically done with a library like jsonwebtoken (Node.js), PyJWT (Python), or the native JWT handling in your framework.
Here is how JWTs typically work in a web application:
Authorization header of subsequent requests: Bearer <token>.This stateless approach eliminates the need for server-side session storage, which is why JWTs are so popular in microservice architectures and single-page applications.
JWTs are powerful, but they introduce security risks if used incorrectly. Here are the most common mistakes developers make:
"alg": "none", which means the token has no signature at all. Attackers can forge tokens by changing the algorithm to "none" and stripping the signature. Always validate that the algorithm matches what you expect.exp claim are valid forever. Always set a reasonable expiration time. Short-lived access tokens (15–30 minutes) paired with longer refresh tokens are a common best practice.Pro tip: Use a dedicated JWT decoder tool to inspect tokens during development and debugging. It is much faster than writing custom decode logic each time, and a good decoder will also flag common security issues like missing expiration or suspicious algorithms.
JWTs are not always the right choice. Here is a quick comparison:
In practice, many applications use a hybrid approach: short-lived JWTs for API authentication and server-side sessions for the web UI.
Yes. The header and payload are base64url-encoded, which is encoding (not encryption). Anyone who intercepts the token can decode it and read the claims. This is by design — JWTs are meant to be self-contained and verifiable, not secret. If you need to protect the data, encrypt it separately or use JWE (JSON Web Encryption) instead of JWT.
JWS (JSON Web Signature) is what most people mean when they say "JWT" — it provides integrity through a signature. JWE (JSON Web Encryption) provides confidentiality by encrypting the payload. A JWE token has five parts instead of three. Use JWS for authentication (you want to verify, not hide, the claims) and JWE when you need to keep the payload private.
Access tokens should be short-lived — 15 to 30 minutes is standard. This limits the damage if a token is compromised. Use a separate refresh token (stored in an HTTP-only cookie) to obtain new access tokens. Refresh tokens can have longer lifetimes (days or weeks) and should be validated against a server-side store to allow revocation.
If the signature does not match, the server will reject the token. This is the entire point of the signature — it ensures that any modification to the header or payload is detected. An attacker cannot change the payload (e.g., changing "role": "user" to "role": "admin") without invalidating the signature, unless they also have access to the signing key.
Not directly, since JWTs are stateless — the server does not store them. This is a tradeoff. To implement revocation, you need to either maintain a blacklist of revoked token IDs (usually stored in Redis with TTL matching the token expiration), use short token lifetimes, or implement a hybrid approach where the server checks a revocation list alongside the token.
Almost, but with two differences: + is replaced with - and / is replaced with _. Also, the trailing = padding is removed. This makes the encoding URL-safe, which is necessary because JWTs are often transmitted in URLs and HTTP headers where special characters would cause problems.
Understanding JWTs is not optional for modern web developers — they are embedded in nearly every authentication system built in the last decade. Knowing how to decode and inspect a token helps you debug auth issues faster, write more secure code, and understand exactly what your application is sending and receiving.
Try Our Free JWT Decoder →Paste any token, see the decoded header and payload instantly, and verify whether the signature is valid. Whether you are debugging an OAuth flow, inspecting an API response, or auditing your own authentication system, a reliable JWT decoder is an essential tool in your developer toolkit.